From 8ac183b3b08975dc2b24b36d267117c702500ec6 Mon Sep 17 00:00:00 2001 From: nyov Date: Mon, 2 Jul 2018 21:38:22 +0000 Subject: [PATCH 01/21] Import Pound v2.8 from vendor --- CHANGELOG | 12 ++++++++++++ Makefile.in | 2 +- config.c | 4 ---- configure | 29 +++++++++-------------------- http.c | 29 +---------------------------- pound.c | 1 + pound.h | 2 +- svc.c | 3 --- 8 files changed, 25 insertions(+), 57 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index b87f4dc..4817390 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,15 @@ +------------------------------------------------------------------------ +r83 | roseg | 2018-05-11 12:16:05 +0200 (Fri, 11 May 2018) | 9 lines + +Release 2.8 + +Enhancements: + - removed DynScale flag and support + - removed support for multi-line headers (both input and output) + +Bug fixes: + - fixed potential request smuggling via fudged headers + ------------------------------------------------------------------------ r82 | roseg | 2016-10-23 16:59:47 +0200 (Sun, 23 Oct 2016) | 8 lines diff --git a/Makefile.in b/Makefile.in index a5af21c..82124cb 100644 --- a/Makefile.in +++ b/Makefile.in @@ -27,7 +27,7 @@ CC=@PTHREAD_CC@ CFLAGS=-DF_CONF=\"@sysconfdir@/pound.cfg\" -DVERSION=\"@PACKAGE_VERSION@\" -DC_SSL=\"@C_SSL@\" -DC_T_RSA=\"@C_T_RSA@\" \ -DC_DH_LEN=\"@C_DH_LEN@\" -DC_MAXBUF=\"@C_MAXBUF@\" -DC_OWNER=\"@C_OWNER@\" -DC_GROUP=\"@C_GROUP@\" \ - -DC_SUPER=\"@C_SUPER@\" -DC_CERT1L=\"@C_CERT1L@\" @CFLAGS@ @PTHREAD_CFLAGS@ @CPPFLAGS@ + -DC_SUPER=\"@C_SUPER@\" @CFLAGS@ @PTHREAD_CFLAGS@ @CPPFLAGS@ LIBS=@LIBS@ @PTHREAD_LIBS@ prefix=@prefix@ diff --git a/config.c b/config.c index d41a3ee..5c47a9f 100644 --- a/config.c +++ b/config.c @@ -1529,10 +1529,6 @@ config_parse(const int argc, char **const argv) if(strcmp(C_SUPER, "0")) logmsg(LOG_DEBUG, " --disable-super"); #endif -#ifdef C_CERT1L - if(strcmp(C_CERT1L, "1")) - logmsg(LOG_DEBUG, " --enable-cert1l"); -#endif #ifdef C_SSL if(strcmp(C_SSL, "")) logmsg(LOG_DEBUG, " --with-ssl=%s", C_SSL); diff --git a/configure b/configure index 1e82480..9cabfa9 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for pound 2.8a. +# Generated by GNU Autoconf 2.69 for pound 2.8. # # Report bugs to . # @@ -580,8 +580,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='pound' PACKAGE_TARNAME='pound' -PACKAGE_VERSION='2.8a' -PACKAGE_STRING='pound 2.8a' +PACKAGE_VERSION='2.8' +PACKAGE_STRING='pound 2.8' PACKAGE_BUGREPORT='roseg@apsis.ch' PACKAGE_URL='' @@ -630,7 +630,6 @@ LIBOBJS EGREP GREP CPP -C_CERT1L C_SUPER C_GROUP C_OWNER @@ -713,7 +712,6 @@ with_maxbuf with_owner with_group enable_super -enable_cert1l enable_pcreposix enable_tcmalloc enable_hoard @@ -1277,7 +1275,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures pound 2.8a to adapt to many kinds of systems. +\`configure' configures pound 2.8 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1344,7 +1342,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of pound 2.8a:";; + short | recursive ) echo "Configuration of pound 2.8:";; esac cat <<\_ACEOF @@ -1354,8 +1352,6 @@ Optional Features: --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --enable-super enable or disable running with supervisor process (default: enabled) - --enable-cert1l enable or disable single-line certificate (default: - disabled) --enable-pcreposix enable or disable using the pcreposix library (default: enabled if available) --enable-tcmalloc enable or disable using the tcmalloc library @@ -1451,7 +1447,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -pound configure 2.8a +pound configure 2.8 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -1874,7 +1870,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by pound $as_me 2.8a, which was +It was created by pound $as_me 2.8, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -3829,13 +3825,6 @@ fi -# Check whether --enable-cert1l was given. -if test "${enable_cert1l+set}" = set; then : - enableval=$enable_cert1l; test ${enableval} = "yes" && CFLAGS="${CFLAGS} -DCERT1L"; C_CERT1L="1" -fi - - - # Check whether --enable-pcreposix was given. if test "${enable_pcreposix+set}" = set; then : enableval=$enable_pcreposix; C_PCREPOSIX=${enableval} @@ -6183,7 +6172,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by pound $as_me 2.8a, which was +This file was extended by pound $as_me 2.8, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -6245,7 +6234,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -pound config.status 2.8a +pound config.status 2.8 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git a/http.c b/http.c index dd211e4..ec091c0 100644 --- a/http.c +++ b/http.c @@ -1136,7 +1136,6 @@ do_http(thr_arg *arg) clean_all(); return; } -#ifdef CERT1L PEM_write_bio_X509(bb, x509); get_line(bb, buf, MAXBUF); if(BIO_printf(be, "X-SSL-certificate: %s", buf) <= 0) { @@ -1161,7 +1160,7 @@ do_http(thr_arg *arg) return; } } - if(BIO_printf(be, "\r\n", buf) <= 0) { + if(BIO_printf(be, "\r\n") <= 0) { str_be(buf, MAXBUF - 1, cur_backend); end_req = cur_time(); logmsg(LOG_WARNING, "(%lx) e500 error write X-SSL-certificate to %s: %s (%.3f sec)", @@ -1171,32 +1170,6 @@ do_http(thr_arg *arg) clean_all(); return; } -#else - PEM_write_bio_X509(bb, x509); - get_line(bb, buf, MAXBUF); - if(BIO_printf(be, "X-SSL-certificate: %s\r\n", buf) <= 0) { - str_be(buf, MAXBUF - 1, cur_backend); - end_req = cur_time(); - logmsg(LOG_WARNING, "(%lx) e500 error write X-SSL-certificate to %s: %s (%.3f sec)", - pthread_self(), buf, strerror(errno), (end_req - start_req) / 1000000.0); - err_reply(cl, h500, lstn->err500); - BIO_free_all(bb); - clean_all(); - return; - } - while(get_line(bb, buf, MAXBUF) == 0) { - if(BIO_printf(be, "\t%s\r\n", buf) <= 0) { - str_be(buf, MAXBUF - 1, cur_backend); - end_req = cur_time(); - logmsg(LOG_WARNING, "(%lx) e500 error write X-SSL-certificate to %s: %s (%.3f sec)", - pthread_self(), buf, strerror(errno), (end_req - start_req) / 1000000.0); - err_reply(cl, h500, lstn->err500); - BIO_free_all(bb); - clean_all(); - return; - } - } -#endif BIO_free_all(bb); } } diff --git a/pound.c b/pound.c index 3643f70..303da2a 100644 --- a/pound.c +++ b/pound.c @@ -170,6 +170,7 @@ get_thr_arg(void) /* * get the current queue length */ +int get_thr_qlen(void) { int res; diff --git a/pound.h b/pound.h index fa22c36..da40fd5 100644 --- a/pound.h +++ b/pound.h @@ -486,7 +486,7 @@ extern thr_arg *get_thr_arg(void); /* * get the current queue length */ -extern get_thr_qlen(void); +extern int get_thr_qlen(void); /* * handle an HTTP request diff --git a/svc.c b/svc.c index 60ba488..33b72d8 100644 --- a/svc.c +++ b/svc.c @@ -409,9 +409,6 @@ check_header(const char *header, char *const content) return hd_types[i].val; } return HEADER_OTHER; - } else if(header[0] == ' ' || header[0] == '\t') { - *content = '\0'; - return HEADER_OTHER; } else return HEADER_ILLEGAL; } From 66f50dbf25f35e23e689c364d8d34f9a55d5ea3f Mon Sep 17 00:00:00 2001 From: Joe Gooch Date: Mon, 20 Oct 2014 10:19:53 -0400 Subject: [PATCH 02/21] fix file permissions --- FAQ | 0 GPL.txt | 0 README | 0 config.h.in | 0 poundctl.8 | 0 poundctl.c | 0 6 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 FAQ mode change 100755 => 100644 GPL.txt mode change 100755 => 100644 README mode change 100755 => 100644 config.h.in mode change 100755 => 100644 poundctl.8 mode change 100755 => 100644 poundctl.c diff --git a/FAQ b/FAQ old mode 100755 new mode 100644 diff --git a/GPL.txt b/GPL.txt old mode 100755 new mode 100644 diff --git a/README b/README old mode 100755 new mode 100644 diff --git a/config.h.in b/config.h.in old mode 100755 new mode 100644 diff --git a/poundctl.8 b/poundctl.8 old mode 100755 new mode 100644 diff --git a/poundctl.c b/poundctl.c old mode 100755 new mode 100644 From aa01d74f24e69f7b7c6eaefd195d73b3fece17e1 Mon Sep 17 00:00:00 2001 From: Joe Gooch Date: Mon, 20 Oct 2014 10:30:42 -0400 Subject: [PATCH 03/21] Retag release as 2.8+github --- configure | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/configure b/configure index 9cabfa9..e44ecb8 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for pound 2.8. +# Generated by GNU Autoconf 2.69 for pound 2.8+github. # # Report bugs to . # @@ -580,8 +580,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='pound' PACKAGE_TARNAME='pound' -PACKAGE_VERSION='2.8' -PACKAGE_STRING='pound 2.8' +PACKAGE_VERSION='2.8+github' +PACKAGE_STRING='pound 2.8+github' PACKAGE_BUGREPORT='roseg@apsis.ch' PACKAGE_URL='' @@ -1275,7 +1275,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures pound 2.8 to adapt to many kinds of systems. +\`configure' configures pound 2.8+github to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1342,7 +1342,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of pound 2.8:";; + short | recursive ) echo "Configuration of pound 2.8+github:";; esac cat <<\_ACEOF @@ -1447,7 +1447,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -pound configure 2.8 +pound configure 2.8+github generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -1870,7 +1870,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by pound $as_me 2.8, which was +It was created by pound $as_me 2.8+github, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -6172,7 +6172,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by pound $as_me 2.8, which was +This file was extended by pound $as_me 2.8+github, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -6234,7 +6234,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -pound config.status 2.8 +pound config.status 2.8+github configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" From 4b08a903c978f697a9509819f69c69601925c418 Mon Sep 17 00:00:00 2001 From: Joe Gooch Date: Tue, 26 Mar 2013 07:46:04 -0400 Subject: [PATCH 04/21] write ipv4 addresses as ipv4 addresses, not ipv6 mapped addresses Based on a patch from the pound mailing list originally by Bussi Andrea ----- If an IPv4 connection is made to an IPv6 socket then the local and remote network addresses will be represented as IPv4-mapped addresses. We try to print the IPv4 addresses in the usual format. From inet_ntop man page: BUGS AF_INET6 converts IPv4-mapped IPv6 addresses into an IPv6 format. So this patch can be considered as a workaround for an inet_ntop bug. --- svc.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/svc.c b/svc.c index 33b72d8..ece50f0 100644 --- a/svc.c +++ b/svc.c @@ -286,8 +286,14 @@ addr2str(char *const res, const int res_len, const struct addrinfo *addr, const case AF_INET6: src = (void *)&((struct sockaddr_in6 *)addr->ai_addr)->sin6_addr.s6_addr; port = ntohs(((struct sockaddr_in6 *)addr->ai_addr)->sin6_port); - if(inet_ntop(AF_INET6, src, buf, MAXBUF - 1) == NULL) - strncpy(buf, "(UNKNOWN)", MAXBUF - 1); + if( IN6_IS_ADDR_V4MAPPED( &(((struct sockaddr_in6 *)addr->ai_addr)->sin6_addr) )) { + src = (void *)&((struct sockaddr_in6 *)addr->ai_addr)->sin6_addr.s6_addr[12]; + if(inet_ntop(AF_INET, src, buf, MAXBUF - 1) == NULL) + strncpy(buf, "(UNKNOWN)", MAXBUF - 1); + } else { + if(inet_ntop(AF_INET6, src, buf, MAXBUF - 1) == NULL) + strncpy(buf, "(UNKNOWN)", MAXBUF - 1); + } break; case AF_UNIX: strncpy(buf, (char *)addr->ai_addr, MAXBUF - 1); From 59eef8365649c141f28a5c9034291d839de42afb Mon Sep 17 00:00:00 2001 From: Joseph Gooch Date: Sun, 5 Feb 2012 00:07:08 -0500 Subject: [PATCH 05/21] Allow setting ownership and mode for the poundctl Control Socket This patch introduces three new options, e.g.: ControlUser "pound" ControlGroup "pound" ControlMode 0600 The directives are read as part of the config, and when pound starts and creates the Control Socket, it will chown and chmod accordingly. --- config.c | 27 +++++++++++++++++++++++++++ pound.8 | 9 +++++++++ pound.c | 37 ++++++++++++++++++++++++++++++++++++- pound.h | 6 +++++- 4 files changed, 77 insertions(+), 2 deletions(-) diff --git a/config.c b/config.c index 5c47a9f..fa45775 100644 --- a/config.c +++ b/config.c @@ -82,6 +82,8 @@ static regex_t ClientCert, AddHeader, DisableProto, SSLAllowClientRenegotiation static regex_t CAlist, VerifyList, CRLlist, NoHTTPS11, Grace, Include, ConnTO, IgnoreCase, HTTPS; static regex_t Disabled, Threads, CNName, Anonymise, ECDHCurve; +static regex_t ControlGroup, ControlUser, ControlMode; + static regmatch_t matches[5]; static char *xhttp[] = { @@ -1376,6 +1378,25 @@ parse_file(void) conf_err("Control multiply defined - aborted"); lin[matches[1].rm_eo] = '\0'; ctrl_name = strdup(lin + matches[1].rm_so); + } else if(!regexec(&ControlUser, lin, 4, matches, 0)) { + lin[matches[1].rm_eo] = '\0'; + if((ctrl_user = strdup(lin + matches[1].rm_so)) == NULL) { + logmsg(LOG_ERR, "line %d: ControlUser config: out of memory - aborted", n_lin); + exit(1); + } + } else if(!regexec(&ControlGroup, lin, 4, matches, 0)) { + lin[matches[1].rm_eo] = '\0'; + if((ctrl_group = strdup(lin + matches[1].rm_so)) == NULL) { + logmsg(LOG_ERR, "line %d: ControlGroup config: out of memory - aborted", n_lin); + exit(1); + } + } else if(!regexec(&ControlMode, lin, 4, matches, 0)) { + lin[matches[1].rm_eo] = '\0'; + ctrl_mode = strtol(lin+matches[1].rm_so, NULL, 8); + if(errno==ERANGE || errno==EINVAL) { + logmsg(LOG_ERR, "line %d: ControlMode config: %s - aborted", n_lin, strerror(errno)); + exit(1); + } } else if(!regexec(&ListenHTTP, lin, 4, matches, 0)) { if(listeners == NULL) listeners = parse_HTTP(); @@ -1441,6 +1462,9 @@ config_parse(const int argc, char **const argv) || regcomp(&Alive, "^[ \t]*Alive[ \t]+([1-9][0-9]*)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&SSLEngine, "^[ \t]*SSLEngine[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&Control, "^[ \t]*Control[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) + || regcomp(&ControlUser, "^[ \t]*ControlUser[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) + || regcomp(&ControlGroup, "^[ \t]*ControlGroup[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) + || regcomp(&ControlMode, "^[ \t]*ControlMode[ \t]+([0-7]+)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&ListenHTTP, "^[ \t]*ListenHTTP[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&ListenHTTPS, "^[ \t]*ListenHTTPS[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&End, "^[ \t]*End[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) @@ -1606,6 +1630,9 @@ config_parse(const int argc, char **const argv) regfree(&Alive); regfree(&SSLEngine); regfree(&Control); + regfree(&ControlUser); + regfree(&ControlGroup); + regfree(&ControlMode); regfree(&ListenHTTP); regfree(&ListenHTTPS); regfree(&End); diff --git a/pound.8 b/pound.8 index c98b5cd..f76fc26 100644 --- a/pound.8 +++ b/pound.8 @@ -314,6 +314,15 @@ the .I poundctl(8) program. .TP +\fBControlUser\fR "user" +The username to chown the Control socket to. +.TP +\fBControlGroup\fR "group" +The groupname to chgrp the Control socket to. +.TP +\fBControlMode\fR 0660 +The mode the Control socket should use, in octal. +.TP \fBInclude\fR "/path/to/file" Include the file as though it were part of the configuration file. .TP diff --git a/pound.c b/pound.c index 303da2a..cce85f7 100644 --- a/pound.c +++ b/pound.c @@ -32,7 +32,11 @@ char *user, /* user to run as */ *group, /* group to run as */ *root_jail, /* directory to chroot to */ *pid_name, /* file to record pid in */ - *ctrl_name; /* control socket name */ + *ctrl_name, /* control socket name */ + *ctrl_user, /* control socket username */ + *ctrl_group; /* control socket group name */ + +long ctrl_mode; /* octal mode of the control socket */ int alive_to, /* check interval for resurrection */ anonymise, /* anonymise client address */ @@ -247,6 +251,8 @@ main(const int argc, char **argv) print_log = 0; (void)umask(077); control_sock = -1; + ctrl_user=ctrl_group=NULL; + ctrl_mode = -1; log_facility = -1; logmsg(LOG_NOTICE, "starting..."); @@ -326,6 +332,35 @@ main(const int argc, char **argv) logmsg(LOG_ERR, "Control \"%s\" bind: %s", ctrl.sun_path, strerror(errno)); exit(1); } + if (ctrl_user) { + struct passwd *pw; + + if((pw = getpwnam(ctrl_user)) == NULL) { + logmsg(LOG_ERR, "no such user %s - aborted", ctrl_user); + exit(1); + } + if (chown(ctrl_name, pw->pw_uid, -1)) { + logmsg(LOG_ERR, "chown error on control socket - aborted (%s)", strerror(errno)); + exit(1); + } + } + if (ctrl_group) { + struct group *gr; + if((gr = getgrnam(ctrl_group)) == NULL) { + logmsg(LOG_ERR, "no such group %s - aborted", ctrl_group); + exit(1); + } + if (chown(ctrl_name, -1, gr->gr_gid)) { + logmsg(LOG_ERR, "chown error on control socket - aborted (%s)", strerror(errno)); + exit(1); + } + } + if (ctrl_mode>0) { + if (chmod(ctrl_name, ctrl_mode)) { + logmsg(LOG_ERR, "chmod error on control socket - aborted (%s)", strerror(errno)); + exit(1); + } + } listen(control_sock, 512); } diff --git a/pound.h b/pound.h index da40fd5..36510f0 100644 --- a/pound.h +++ b/pound.h @@ -264,7 +264,11 @@ extern char *user, /* user to run as */ *group, /* group to run as */ *root_jail, /* directory to chroot to */ *pid_name, /* file to record pid in */ - *ctrl_name; /* control socket name */ + *ctrl_name, /* control socket name */ + *ctrl_user, /* control socket username */ + *ctrl_group; /* control socket group name */ + +extern long ctrl_mode; /* octal mode of the control socket */ extern int numthreads, /* number of worker threads */ anonymise, /* anonymise client address */ From ae5b99130477f2f500d2c5f73fdd0432b44da6a9 Mon Sep 17 00:00:00 2001 From: Joe Gooch Date: Mon, 20 Oct 2014 11:02:14 -0400 Subject: [PATCH 06/21] Load DHParameters from config file from a pem file - allows for things like 2048bit DHE --- Makefile.in | 5 ++++- config.c | 26 +++++++++++++++++++++++--- pound.8 | 5 +++++ pound.h | 5 +++++ svc.c | 16 ++++++++++++++++ 5 files changed, 53 insertions(+), 4 deletions(-) diff --git a/Makefile.in b/Makefile.in index 82124cb..7b12f3b 100644 --- a/Makefile.in +++ b/Makefile.in @@ -37,7 +37,7 @@ exec_prefix=@exec_prefix@ OBJS=pound.o http.o config.o svc.o -all: pound poundctl pound.8 +all: pound poundctl pound.8 dh2048.pem pound: $(OBJS) ${CC} @LDFLAGS@ -o pound $(OBJS) $(LIBS) @@ -54,6 +54,9 @@ dh@C_DH_LEN@.h: svc.o: svc.c dh512.h dh@C_DH_LEN@.h ${CC} ${CFLAGS} -c -o svc.o svc.c +dh2048.pem: + openssl dhparam -5 -out dh2048.pem 2048 + $(OBJS) poundctl.o: pound.h config.h install: all diff --git a/config.c b/config.c index fa45775..214c164 100644 --- a/config.c +++ b/config.c @@ -80,12 +80,14 @@ static regex_t Service, ServiceName, URL, HeadRequire, HeadDeny, BackEnd, Emerg static regex_t Redirect, RedirectN, TimeOut, Session, Type, TTL, ID; static regex_t ClientCert, AddHeader, DisableProto, SSLAllowClientRenegotiation, SSLHonorCipherOrder, Ciphers; static regex_t CAlist, VerifyList, CRLlist, NoHTTPS11, Grace, Include, ConnTO, IgnoreCase, HTTPS; -static regex_t Disabled, Threads, CNName, Anonymise, ECDHCurve; +static regex_t Disabled, Threads, CNName, Anonymise, DHParams, ECDHCurve; static regex_t ControlGroup, ControlUser, ControlMode; static regmatch_t matches[5]; +static DH *DHCustom_params; + static char *xhttp[] = { "^(GET|POST|HEAD) ([^ ]+) HTTP/1.[01]$", "^(GET|POST|HEAD|PUT|PATCH|DELETE) ([^ ]+) HTTP/1.[01]$", @@ -361,7 +363,11 @@ parse_be(const int is_emergency) sprintf(lin, "%d-Pound-%ld", getpid(), random()); SSL_CTX_set_session_id_context(res->ctx, (unsigned char *)lin, strlen(lin)); SSL_CTX_set_tmp_rsa_callback(res->ctx, RSA_tmp_callback); - SSL_CTX_set_tmp_dh_callback(res->ctx, DH_tmp_callback); + if (NULL == DHCustom_params) + SSL_CTX_set_tmp_dh_callback(res->ctx, DH_tmp_callback); + else + SSL_CTX_set_tmp_dh(res->ctx, DHCustom_params); + #if OPENSSL_VERSION_NUMBER >= 0x0090800fL #ifndef OPENSSL_NO_ECDH /* This generates a EC_KEY structure with no key, but a group defined */ @@ -1265,8 +1271,12 @@ parse_HTTPS(void) sprintf(lin, "%d-Pound-%ld", getpid(), random()); SSL_CTX_set_session_id_context(pc->ctx, (unsigned char *)lin, strlen(lin)); SSL_CTX_set_tmp_rsa_callback(pc->ctx, RSA_tmp_callback); - SSL_CTX_set_tmp_dh_callback(pc->ctx, DH_tmp_callback); SSL_CTX_set_info_callback(pc->ctx, SSLINFO_callback); + if (NULL == DHCustom_params) + SSL_CTX_set_tmp_dh_callback(pc->ctx, DH_tmp_callback); + else + SSL_CTX_set_tmp_dh(pc->ctx, DHCustom_params); + #if OPENSSL_VERSION_NUMBER >= 0x0090800fL #ifndef OPENSSL_NO_ECDH /* This generates a EC_KEY structure with no key, but a group defined */ @@ -1318,6 +1328,12 @@ parse_file(void) lin[matches[1].rm_eo] = '\0'; if((root_jail = strdup(lin + matches[1].rm_so)) == NULL) conf_err("RootJail config: out of memory - aborted"); + } else if(!regexec(&DHParams, lin, 4, matches, 0)) { + lin[matches[1].rm_eo] = '\0'; + DH *dh = load_dh_params(lin + matches[1].rm_so); + if (!dh) + conf_err("DHParams config: could not load file"); + DHCustom_params = dh; } else if(!regexec(&Daemon, lin, 4, matches, 0)) { daemonize = atoi(lin + matches[1].rm_so); } else if(!regexec(&Threads, lin, 4, matches, 0)) { @@ -1514,6 +1530,7 @@ config_parse(const int argc, char **const argv) || regcomp(&IgnoreCase, "^[ \t]*IgnoreCase[ \t]+([01])[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&HTTPS, "^[ \t]*HTTPS[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&Disabled, "^[ \t]*Disabled[ \t]+([01])[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) + || regcomp(&DHParams, "^[ \t]*DHParams[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&CNName, ".*[Cc][Nn]=([-*.A-Za-z0-9]+).*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&Anonymise, "^[ \t]*Anonymise[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) #if OPENSSL_VERSION_NUMBER >= 0x0090800fL @@ -1596,6 +1613,7 @@ config_parse(const int argc, char **const argv) group = NULL; root_jail = NULL; ctrl_name = NULL; + DHCustom_params = NULL; numthreads = 128; alive_to = 30; @@ -1689,6 +1707,8 @@ config_parse(const int argc, char **const argv) regfree(&ECDHCurve); #endif #endif + regfree(&DHParams); + if (DHCustom_params) DH_free(DHCustom_params); /* set the facility only here to ensure the syslog gets opened if necessary */ log_facility = def_facility; diff --git a/pound.8 b/pound.8 index f76fc26..b1715d2 100644 --- a/pound.8 +++ b/pound.8 @@ -241,6 +241,11 @@ the facility name causes .B Pound to log to stdout/stderr. .TP +\fBDHParams\fR "path/to/dhparams.pem" +Use the supplied dhparams pem file for DH key exchange for non-export-controlled +negotiations. Generate such a file with \fBopenssl dhparam\fR. +This can be used to do 2048bit DHE. +.TP \fBLogLevel\fR value Specify the logging level: 0 for no logging, 1 (default) for regular logging, 2 for extended logging (show chosen backend server as well), diff --git a/pound.h b/pound.h index 36510f0..a58decb 100644 --- a/pound.h +++ b/pound.h @@ -587,6 +587,11 @@ extern void config_parse(const int, char **const); */ extern RSA *RSA_tmp_callback(SSL *, int, int); +/* + * handle custom DH keys + */ +extern DH *load_dh_params(char *); + /* * return a pre-generated RSA key */ diff --git a/svc.c b/svc.c index ece50f0..1f74fe4 100644 --- a/svc.c +++ b/svc.c @@ -1690,3 +1690,19 @@ SSLINFO_callback(const SSL *ssl, int where, int rc) *reneg_state = RENEG_REJECT; } } + +DH *load_dh_params(char *file) +{ + DH *dh = NULL; + BIO *bio; + + if ((bio = BIO_new_file(file, "r")) == NULL) { + logmsg(LOG_WARNING, "Unable to open DH file - %s" ,file); + return NULL; + } + + dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); + BIO_free(bio); + return dh; +} + From 2e321fc83ec4a318006c9d06f58c79564f97f2ed Mon Sep 17 00:00:00 2001 From: Joe Gooch Date: Mon, 20 Oct 2014 11:03:42 -0400 Subject: [PATCH 07/21] Make HTTP/HTTPS mismatch errors more friendly Original patch from http://hg.openpanel.com/pound-sslpatched/raw-rev/90a2b1ae6bf4 by Peter van Dijk This patch adds two directives: ErrNoSSL "my_friendly_error.html" NoSSlRedirect "https://www.website.com" Primarily this patch is concerned with what happens when a user tries to connect to an SSL port with a non-SSL connection. For instance, http://www.website.com:443/ This cannot currently be controlled in pound, since the port is SSL, there's no way to redirect, and in fact the openssl library will fail negotiation so there's no opportunity for a friendly error unless we trap that case directly. Using the NoSslredirect directive you can redirect to a https url. Otherwise, ErrNoSSL will show in the browser window, alerting the user to the problem. --- config.c | 27 ++++++++++++++++++++++++++- http.c | 23 +++++++++++++++-------- pound.8 | 18 ++++++++++++++++++ pound.h | 5 ++++- 4 files changed, 63 insertions(+), 10 deletions(-) diff --git a/config.c b/config.c index 214c164..488b2d6 100644 --- a/config.c +++ b/config.c @@ -75,7 +75,7 @@ static CODE facilitynames[] = { static regex_t Empty, Comment, User, Group, RootJail, Daemon, LogFacility, LogLevel, Alive, SSLEngine, Control; static regex_t ListenHTTP, ListenHTTPS, End, Address, Port, Cert, xHTTP, Client, CheckURL; -static regex_t Err414, Err500, Err501, Err503, MaxRequest, HeadRemove, RewriteLocation, RewriteDestination; +static regex_t Err414, Err500, Err501, Err503, ErrNoSsl, NoSslRedirect, MaxRequest, HeadRemove, RewriteLocation, RewriteDestination; static regex_t Service, ServiceName, URL, HeadRequire, HeadDeny, BackEnd, Emergency, Priority, HAport, HAportAddr; static regex_t Redirect, RedirectN, TimeOut, Session, Type, TTL, ID; static regex_t ClientCert, AddHeader, DisableProto, SSLAllowClientRenegotiation, SSLHonorCipherOrder, Ciphers; @@ -760,6 +760,9 @@ parse_HTTP(void) res->err500 = "An internal server error occurred. Please try again later."; res->err501 = "This method may not be used."; res->err503 = "The service is not available. Please try again later."; + res->errnossl= "Please use HTTPS."; + res->nossl_url = NULL; + res->nossl_redir = 0; res->log_level = log_level; if(regcomp(&res->verb, xhttp[0], REG_ICASE | REG_NEWLINE | REG_EXTENDED)) conf_err("xHTTP bad default pattern - aborted"); @@ -964,6 +967,9 @@ parse_HTTPS(void) res->err501 = "This method may not be used."; res->err503 = "The service is not available. Please try again later."; res->allow_client_reneg = 0; + res->errnossl = "Please use HTTPS."; + res->nossl_url = NULL; + res->nossl_redir = 0; res->log_level = log_level; if(regcomp(&res->verb, xhttp[0], REG_ICASE | REG_NEWLINE | REG_EXTENDED)) conf_err("xHTTP bad default pattern - aborted"); @@ -1017,6 +1023,21 @@ parse_HTTPS(void) } else if(!regexec(&Err503, lin, 4, matches, 0)) { lin[matches[1].rm_eo] = '\0'; res->err503 = file2str(lin + matches[1].rm_so); + } else if(!regexec(&ErrNoSsl, lin, 4, matches, 0)) { + lin[matches[1].rm_eo] = '\0'; + res->errnossl = file2str(lin + matches[1].rm_so); + } else if(!regexec(&NoSslRedirect, lin, 4, matches, 0)) { + res->nossl_redir = 302; + if (matches[1].rm_eo != matches[1].rm_so) + res->nossl_redir = atoi(lin + matches[1].rm_so); + lin[matches[2].rm_eo] = '\0'; + if((res->nossl_url = strdup(lin + matches[2].rm_so))==NULL) + conf_err("NoSslRedirect out of memory"); + if(regexec(&LOCATION, res->nossl_url, 4, matches, 0)) + conf_err("Redirect bad URL - aborted"); + if((matches[3].rm_eo - matches[3].rm_so) == 1) + /* the path is a single '/', so remove it */ + res->nossl_url[matches[3].rm_so] = '\0'; } else if(!regexec(&MaxRequest, lin, 4, matches, 0)) { res->max_req = ATOL(lin + matches[1].rm_so); } else if(!regexec(&HeadRemove, lin, 4, matches, 0)) { @@ -1494,6 +1515,8 @@ config_parse(const int argc, char **const argv) || regcomp(&Err500, "^[ \t]*Err500[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&Err501, "^[ \t]*Err501[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&Err503, "^[ \t]*Err503[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) + || regcomp(&ErrNoSsl, "^[ \t]*ErrNoSsl[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) + || regcomp(&NoSslRedirect, "^[ \t]*NoSslRedirect[ \t]+(30[127][ \t]+)?\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&MaxRequest, "^[ \t]*MaxRequest[ \t]+([1-9][0-9]*)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&HeadRemove, "^[ \t]*HeadRemove[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&RewriteLocation, "^[ \t]*RewriteLocation[ \t]+([012])[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) @@ -1664,6 +1687,8 @@ config_parse(const int argc, char **const argv) regfree(&Err500); regfree(&Err501); regfree(&Err503); + regfree(&ErrNoSsl); + regfree(&NoSslRedirect); regfree(&MaxRequest); regfree(&HeadRemove); regfree(&RewriteLocation); diff --git a/http.c b/http.c index ec091c0..36a969b 100644 --- a/http.c +++ b/http.c @@ -32,7 +32,7 @@ static char *h500 = "500 Internal Server Error", *h501 = "501 Not Implemented", *h503 = "503 Service Unavailable", *h414 = "414 Request URI too long", - *h400 = "Bad Request"; + *h400 = "400 Bad Request"; static char *err_response = "HTTP/1.0 %s\r\nContent-Type: text/html\r\nContent-Length: %d\r\nExpires: now\r\nPragma: no-cache\r\nCache-control: no-cache,no-store\r\n\r\n%s"; @@ -547,7 +547,7 @@ do_http(thr_arg *arg) BACKEND *backend, *cur_backend, *old_backend; struct addrinfo from_host, z_addr; struct sockaddr_storage from_host_addr; - BIO *cl, *be, *bb, *b64; + BIO *oldcl, *cl, *be, *bb, *b64; X509 *x509; char request[MAXBUF], response[MAXBUF], buf[MAXBUF], url[MAXBUF], loc_path[MAXBUF], **headers, headers_ok[MAXHEADERS], v_host[MAXBUF], referer[MAXBUF], u_agent[MAXBUF], u_name[MAXBUF], @@ -620,14 +620,21 @@ do_http(thr_arg *arg) } BIO_set_ssl(bb, ssl, BIO_CLOSE); BIO_set_ssl_mode(bb, 0); + + oldcl = cl; cl = bb; if(BIO_do_handshake(cl) <= 0) { - /* no need to log every client without a certificate... - addr2str(caddr, MAXBUF - 1, &from_host, 1); - logmsg(LOG_NOTICE, "BIO_do_handshake with %s failed: %s", caddr, - ERR_error_string(ERR_get_error(), NULL)); - x509 = NULL; - */ + if ((ERR_GET_REASON(ERR_peek_error()) == SSL_R_HTTP_REQUEST) + && (ERR_GET_LIB(ERR_peek_error()) == ERR_LIB_SSL)) { + addr2str(caddr, MAXBUF - 1, &from_host, 1); + if (lstn->nossl_redir) { + logmsg(LOG_NOTICE, "(%lx) errNoSsl from %s redirecting to \"%s\"", pthread_self(), caddr, lstn->nossl_url); + redirect_reply(oldcl, lstn->nossl_url, lstn->nossl_redir); + } else { + logmsg(LOG_NOTICE, "(%lx) errNoSsl from %s sending error", pthread_self(), caddr); + err_reply(oldcl, h400, lstn->errnossl); + } + } BIO_reset(cl); BIO_free_all(cl); return; diff --git a/pound.8 b/pound.8 index b1715d2..32d4f1c 100644 --- a/pound.8 +++ b/pound.8 @@ -416,6 +416,24 @@ Default: "This method may not be used.". A file with the text to be displayed if an Error 503 occurs. Default: "The service is not available. Please try again later.". .TP +\fBErrNoSsl\fR "filename" +A file with the text to be displayed if a user connects to a HTTPS listener with HTTP. +Default: "Please use HTTPS.". + +Only valid for HTTPS listeners. +.TP +\fBNoSslRedirect\fR [code] "url" +A url that the user will be redirected to if the user connects to a HTTPS listener with HTTP. +.br +The code here is just like the code in Redirect blocks. It defaults to 302, but could be 301 or 307. +.br +Only valid for HTTPS listeners. +.br +Example: +.IP +.br +NoSslRedirect "https://thishost:port" +.TP \fBMaxRequest\fR nnn Request maximal size. All requests will be limited to these many bytes. If a request contains more data than allowed an error 414 is returned. Default: diff --git a/pound.h b/pound.h index a58decb..6a9ddc8 100644 --- a/pound.h +++ b/pound.h @@ -403,7 +403,10 @@ typedef struct _listener { char *err414, /* error messages */ *err500, *err501, - *err503; + *err503, + *errnossl; + char *nossl_url; /* If a user goes to a https port with a http: url, redirect them to this url */ + int nossl_redir; /* Code to use for redirect (301 302 307)*/ LONG max_req; /* max. request size */ MATCHER *head_off; /* headers to remove */ int rewr_loc; /* rewrite location response */ From a2739d903c6611eeba799e4ae694baf4fd938d9a Mon Sep 17 00:00:00 2001 From: Joe Gooch Date: Tue, 18 Jun 2013 13:49:06 -0400 Subject: [PATCH 08/21] Implement backend session flushing --- pound.h | 2 +- poundctl.c | 18 ++++++++++++++++-- svc.c | 15 +++++++++++++++ 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/pound.h b/pound.h index 6a9ddc8..b1b88be 100644 --- a/pound.h +++ b/pound.h @@ -454,7 +454,7 @@ typedef enum { CTRL_EN_LSTN, CTRL_DE_LSTN, CTRL_EN_SVC, CTRL_DE_SVC, CTRL_EN_BE, CTRL_DE_BE, - CTRL_ADD_SESS, CTRL_DEL_SESS + CTRL_ADD_SESS, CTRL_DEL_SESS, CTRL_FLUSH_SESS } CTRL_CODE; typedef struct { diff --git a/poundctl.c b/poundctl.c index ed6d2a7..2ceb8ea 100644 --- a/poundctl.c +++ b/poundctl.c @@ -41,6 +41,7 @@ usage(const char *arg0) fprintf(stderr, "\t-s n m - disable service m in service n (use -1 for global services)\n"); fprintf(stderr, "\t-B n m r - enable back-end r in service m in listener n\n"); fprintf(stderr, "\t-b n m r - disable back-end r in service m in listener n\n"); + fprintf(stderr, "\t-f n m r - flush all sessions for back-end r in service m in listener n\n"); fprintf(stderr, "\t-N n m k r - add a session with key k and back-end r in service m in listener n\n"); fprintf(stderr, "\t-n n m k - remove a session with key k r in service m in listener n\n"); fprintf(stderr, "\n"); @@ -216,7 +217,7 @@ main(const int argc, char **argv) CTRL_CMD cmd; int sock, n_lstn, n_svc, n_be, n_sess, i; char *arg0, *sock_name, buf[KEY_SIZE + 1]; - int c_opt, en_lst, de_lst, en_svc, de_svc, en_be, de_be, a_sess, d_sess, is_set; + int c_opt, en_lst, de_lst, en_svc, de_svc, en_be, de_be, a_sess, d_sess, f_sess, is_set; LISTENER lstn; SERVICE svc; BACKEND be; @@ -225,7 +226,7 @@ main(const int argc, char **argv) arg0 = *argv; sock_name = NULL; - en_lst = de_lst = en_svc = de_svc = en_be = de_be = is_set = a_sess = d_sess = 0; + en_lst = de_lst = en_svc = de_svc = en_be = de_be = is_set = a_sess = d_sess = f_sess = 0; memset(&cmd, 0, sizeof(cmd)); opterr = 0; i = 0; @@ -277,6 +278,11 @@ main(const int argc, char **argv) usage(arg0); d_sess = is_set = 1; break; + case 'f': + if(is_set) + usage(arg0); + f_sess = is_set = 1; + break; case 'H': host_names = 1; break; @@ -332,6 +338,14 @@ main(const int argc, char **argv) cmd.service = atoi(argv[optind++]); strncpy(cmd.key, argv[optind++], KEY_SIZE); } + if(f_sess) { + if(optind != (argc - 3)) + usage(arg0); + cmd.cmd = CTRL_FLUSH_SESS; + cmd.listener = atoi(argv[optind++]); + cmd.service = atoi(argv[optind++]); + cmd.backend = atoi(argv[optind++]); + } if(!is_set) { if(optind != argc) usage(arg0); diff --git a/svc.c b/svc.c index 1f74fe4..9f84042 100644 --- a/svc.c +++ b/svc.c @@ -1659,6 +1659,21 @@ thr_control(void *arg) if(ret_val = pthread_mutex_unlock(&svc->mut)) logmsg(LOG_WARNING, "thr_control() del session unlock: %s", strerror(ret_val)); break; + case CTRL_FLUSH_SESS: + if((svc = sel_svc(&cmd)) == NULL) { + logmsg(LOG_INFO, "thr_control() bad service %d/%d", cmd.listener, cmd.service); + break; + } + if((be = sel_be(&cmd)) == NULL) { + logmsg(LOG_INFO, "thr_control() bad backend %d/%d/%d", cmd.listener, cmd.service, cmd.backend); + break; + } + if(ret_val = pthread_mutex_lock(&svc->mut)) + logmsg(LOG_WARNING, "thr_control() del session lock: %s", strerror(ret_val)); + t_clean(svc->sessions, &be, sizeof(be)); + if(ret_val = pthread_mutex_unlock(&svc->mut)) + logmsg(LOG_WARNING, "thr_control() del session unlock: %s", strerror(ret_val)); + break; default: logmsg(LOG_WARNING, "thr_control() unknown command"); break; From 59c477e1bacff6b474904e194be5103b3f0ddfc9 Mon Sep 17 00:00:00 2001 From: Joe Gooch Date: Mon, 19 Mar 2012 10:58:34 -0400 Subject: [PATCH 09/21] ThreadModel implementation Implement a config option for pool (2.6 behavior) vs dynamic (2.5 behavior) Revise threadpool implementation to eliminate spurious wake-up issues and get_thr_arg NULL warnings. Thanks to Jacob Anderson for pthread suggestions --- config.c | 6 ++++++ http.c | 14 +++++++++++- pound.8 | 13 ++++++++++++ pound.c | 65 +++++++++++++++++++++++++++++++++++++------------------- pound.h | 5 ++++- 5 files changed, 79 insertions(+), 24 deletions(-) diff --git a/config.c b/config.c index 488b2d6..54619d8 100644 --- a/config.c +++ b/config.c @@ -83,6 +83,7 @@ static regex_t CAlist, VerifyList, CRLlist, NoHTTPS11, Grace, Include, ConnTO, static regex_t Disabled, Threads, CNName, Anonymise, DHParams, ECDHCurve; static regex_t ControlGroup, ControlUser, ControlMode; +static regex_t ThreadModel; static regmatch_t matches[5]; @@ -1359,6 +1360,8 @@ parse_file(void) daemonize = atoi(lin + matches[1].rm_so); } else if(!regexec(&Threads, lin, 4, matches, 0)) { numthreads = atoi(lin + matches[1].rm_so); + } else if(!regexec(&ThreadModel, lin, 4, matches, 0)) { + threadpool = ((lin[matches[1].rm_so]|0x20) == 'p'); /* 'pool' */ } else if(!regexec(&LogFacility, lin, 4, matches, 0)) { lin[matches[1].rm_eo] = '\0'; if(lin[matches[1].rm_so] == '-') @@ -1493,6 +1496,7 @@ config_parse(const int argc, char **const argv) || regcomp(&RootJail, "^[ \t]*RootJail[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&Daemon, "^[ \t]*Daemon[ \t]+([01])[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&Threads, "^[ \t]*Threads[ \t]+([1-9][0-9]*)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) + || regcomp(&ThreadModel, "^[ \t]*ThreadModel[ \t]+(pool|dynamic)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&LogFacility, "^[ \t]*LogFacility[ \t]+([a-z0-9-]+)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&LogLevel, "^[ \t]*LogLevel[ \t]+([0-5])[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&Grace, "^[ \t]*Grace[ \t]+([0-9]+)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) @@ -1639,6 +1643,7 @@ config_parse(const int argc, char **const argv) DHCustom_params = NULL; numthreads = 128; + threadpool = 1; alive_to = 30; daemonize = 1; grace = 30; @@ -1665,6 +1670,7 @@ config_parse(const int argc, char **const argv) regfree(&RootJail); regfree(&Daemon); regfree(&Threads); + regfree(&ThreadModel); regfree(&LogFacility); regfree(&LogLevel); regfree(&Grace); diff --git a/http.c b/http.c index 36a969b..2515508 100644 --- a/http.c +++ b/http.c @@ -1657,7 +1657,19 @@ do_http(thr_arg *arg) } void * -thr_http(void *dummy) +thr_http_single(void *dummy) +{ + thr_arg *arg; + + arg = (thr_arg *)dummy; + if (arg == NULL) + logmsg(LOG_NOTICE, "NULL get_thr_arg"); + else + do_http(arg); +} + +void * +thr_http_pool(void *dummy) { thr_arg *arg; diff --git a/pound.8 b/pound.8 index 32d4f1c..54b935a 100644 --- a/pound.8 +++ b/pound.8 @@ -222,6 +222,19 @@ puts itself in the background). By specifying this option you can force to work like a regular process. Useful for debugging or if you want to use something like \fIdaemontools\fR. .TP +\fBThreadModel\fR [pool|dynamic] +If set to pool, +.B Pound +will spawn +\fBThreads\fR +number of threads when it starts and reuse those threads throughout its +lifetime. This sets a hard limit on the number of concurrent requests +that can be processed. +If set to dynamic, a new thread is spawned for each and every request. +This means you have a penalty for thread creation on every request, and +possibility of DoS if too many requests come in, but it also means your +thread count will scale up and down as necessary. +.TP \fBThreads\fR nnn How many worker threads .B Pound diff --git a/pound.c b/pound.c index cce85f7..921feda 100644 --- a/pound.c +++ b/pound.c @@ -115,7 +115,7 @@ l_id(void) static thr_arg *first = NULL, *last = NULL; static pthread_cond_t arg_cond; static pthread_mutex_t arg_mut; -int numthreads; +int numthreads, threadpool; static void init_thr_arg(void) @@ -125,20 +125,29 @@ init_thr_arg(void) return; } -/* - * add a request to the queue - */ -int -put_thr_arg(thr_arg *arg) +thr_arg * +get_dyn_thr_arg(thr_arg *arg) { thr_arg *res; if((res = malloc(sizeof(thr_arg))) == NULL) { logmsg(LOG_WARNING, "thr_arg malloc"); - return -1; + return NULL; } memcpy(res, arg, sizeof(thr_arg)); res->next = NULL; + return res; +} + +/* + * add a request to the queue + */ +int +put_thr_arg(thr_arg *arg) +{ + thr_arg *res; + if ((res = get_dyn_thr_arg(arg))==NULL) + return -1; (void)pthread_mutex_lock(&arg_mut); if(last == NULL) first = last = res; @@ -147,7 +156,7 @@ put_thr_arg(thr_arg *arg) last = last->next; } (void)pthread_mutex_unlock(&arg_mut); - pthread_cond_signal(&arg_cond); + if (threadpool) pthread_cond_signal(&arg_cond); return 0; } @@ -160,13 +169,12 @@ get_thr_arg(void) thr_arg *res; (void)pthread_mutex_lock(&arg_mut); - if(first == NULL) + while((res = first) == NULL) (void)pthread_cond_wait(&arg_cond, &arg_mut); - if((res = first) != NULL) - if((first = first->next) == NULL) - last = NULL; + if((first = first->next) == NULL) + last = NULL; (void)pthread_mutex_unlock(&arg_mut); - if(first != NULL) + if(res->next != NULL) pthread_cond_signal(&arg_cond); return res; } @@ -510,12 +518,14 @@ main(const int argc, char **argv) /* pause to make sure the service threads were started */ sleep(1); - /* create the worker threads */ - for(i = 0; i < numthreads; i++) - if(pthread_create(&thr, &attr, thr_http, NULL)) { - logmsg(LOG_ERR, "create thr_http: %s - aborted", strerror(errno)); - exit(1); - } + if (threadpool) { + /* create the worker threads */ + for(i = 0; i < numthreads; i++) + if(pthread_create(&thr, &attr, thr_http_pool, NULL)) { + logmsg(LOG_ERR, "create thr_http: %s - aborted", strerror(errno)); + exit(1); + } + } /* pause to make sure at least some of the worker threads were started */ sleep(1); @@ -550,7 +560,7 @@ main(const int argc, char **argv) logmsg(LOG_WARNING, "HTTP accept: %s", strerror(errno)); } else if(((struct sockaddr_in *)&clnt_addr)->sin_family == AF_INET || ((struct sockaddr_in *)&clnt_addr)->sin_family == AF_INET6) { - thr_arg arg; + thr_arg arg, *argp; if(lstn->disabled) { /* @@ -572,8 +582,19 @@ main(const int argc, char **argv) arg.from_host.ai_family = AF_INET; else arg.from_host.ai_family = AF_INET6; - if(put_thr_arg(&arg)) - close(clnt); + if(threadpool) { + if(put_thr_arg(&arg)) + close(clnt); + } else { + if((argp = get_dyn_thr_arg(&arg)) == NULL) + close(clnt); + else if(pthread_create(&thr, &attr, thr_http_single, (void*)argp)) { + logmsg(LOG_ERR, "create thr_http: %s - aborted", strerror(errno)); + free(argp->from_host.ai_addr); + free(argp); + close(clnt); + } + } } else { /* may happen on FreeBSD, I am told */ logmsg(LOG_WARNING, "HTTP connection prematurely closed by peer"); diff --git a/pound.h b/pound.h index b1b88be..b24da89 100644 --- a/pound.h +++ b/pound.h @@ -272,6 +272,8 @@ extern long ctrl_mode; /* octal mode of the control socket */ extern int numthreads, /* number of worker threads */ anonymise, /* anonymise client address */ + threadpool, /* 1 to use a threadpool (i.e. 2.6 behavior) + 0 to use new thread per request (2.5 behavior) */ alive_to, /* check interval for resurrection */ daemonize, /* run as daemon */ log_facility, /* log facility to use */ @@ -498,7 +500,8 @@ extern int get_thr_qlen(void); /* * handle an HTTP request */ -extern void *thr_http(void *); +extern void *thr_http_single(void *); +extern void *thr_http_pool(void *); /* * Log an error to the syslog or to stderr From af03addf7c2cb7d69a77b809e7f71d27869e0d39 Mon Sep 17 00:00:00 2001 From: Joe Gooch Date: Mon, 6 Feb 2012 14:19:51 -0500 Subject: [PATCH 10/21] IncludeDir directive - includes a directory of config files (a la conf.d) Increases the Include LIFO stack to 100 instead of 8 Includes all .cfg and .conf files within a given directory. --- config.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- pound.8 | 5 +++++ pound.h | 1 + 3 files changed, 73 insertions(+), 1 deletion(-) diff --git a/config.c b/config.c index 54619d8..7711e44 100644 --- a/config.c +++ b/config.c @@ -85,6 +85,8 @@ static regex_t Disabled, Threads, CNName, Anonymise, DHParams, ECDHCurve; static regex_t ControlGroup, ControlUser, ControlMode; static regex_t ThreadModel; +static regex_t IncludeDir; + static regmatch_t matches[5]; static DH *DHCustom_params; @@ -109,7 +111,7 @@ static int EC_nid = NID_X9_62_prime256v1; #endif #endif -#define MAX_FIN 8 +#define MAX_FIN 100 static FILE *f_in[MAX_FIN]; static char *f_name[MAX_FIN]; @@ -139,6 +141,63 @@ conf_err(const char *msg) exit(1); } +static void include_dir(const char *conf_path) { + DIR * dp; + struct dirent *de; + + char buf[512]; + char *files[200], *cp; + int filecnt = 0; + int idx,use; + + logmsg(LOG_DEBUG, "Including Dir %s", conf_path); + + if((dp = opendir(conf_path)) == NULL) { + conf_err("can't open IncludeDir directory"); + exit(1); + } + + while((de = readdir(dp))!=NULL) { + if (de->d_name[0] == '.') continue; + if ( (strlen(de->d_name) >= 5 && !strncmp(de->d_name + strlen(de->d_name) - 4, ".cfg", 4)) || + (strlen(de->d_name) >= 6 && !strncmp(de->d_name + strlen(de->d_name) - 5, ".conf", 5)) + ){ + snprintf(buf, sizeof(buf), "%s%s%s", conf_path, (conf_path[strlen(conf_path)-1]=='/')?"":"/", de->d_name); + buf[sizeof(buf)-1] = 0; + if (filecnt == sizeof(files)/sizeof(*files)) { + conf_err("Max config files per directory reached"); + } + if ((files[filecnt++] = strdup(buf)) == NULL) { + conf_err("IncludeDir out of memory"); + } + continue; + } + } + /* We order the list, and include in reverse order, because include_file adds to the top of the list */ + while(filecnt) { + use = 0; + for(idx = 1; idx %s", files[use]); + + // Copied from Include logic + if(cur_fin == (MAX_FIN - 1)) + conf_err("Include nesting too deep"); + cur_fin++; + f_name[cur_fin] = files[use]; + if((f_in[cur_fin] = fopen(files[use], "rt")) == NULL) { + logmsg(LOG_ERR, "%s line %d: Can't open included file %s", f_name[cur_fin], n_lin[cur_fin], files[use]); + exit(1); + } + n_lin[cur_fin] = 0; + files[use] = files[--filecnt]; + } + + closedir(dp); +} + static char * conf_fgets(char *buf, const int max) { @@ -175,6 +234,11 @@ conf_fgets(char *buf, const int max) n_lin[cur_fin] = 0; continue; } + if(!regexec(&IncludeDir, buf, 4, matches, 0)) { + buf[matches[1].rm_eo] = '\0'; + include_dir(buf + matches[1].rm_so); + continue; + } return buf; } } @@ -1553,6 +1617,7 @@ config_parse(const int argc, char **const argv) || regcomp(&CRLlist, "^[ \t]*CRLlist[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&NoHTTPS11, "^[ \t]*NoHTTPS11[ \t]+([0-2])[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&Include, "^[ \t]*Include[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) + || regcomp(&IncludeDir, "^[ \t]*IncludeDir[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&ConnTO, "^[ \t]*ConnTO[ \t]+([1-9][0-9]*)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&IgnoreCase, "^[ \t]*IgnoreCase[ \t]+([01])[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&HTTPS, "^[ \t]*HTTPS[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) @@ -1727,6 +1792,7 @@ config_parse(const int argc, char **const argv) regfree(&CRLlist); regfree(&NoHTTPS11); regfree(&Include); + regfree(&IncludeDir); regfree(&ConnTO); regfree(&IgnoreCase); regfree(&HTTPS); diff --git a/pound.8 b/pound.8 index 54b935a..a6be176 100644 --- a/pound.8 +++ b/pound.8 @@ -347,6 +347,11 @@ Include the file as though it were part of the configuration file. \fBAnonymise\fR Replace the last byte of the client address with 0 for logging purposes. Default: log the client address in full. +\fBIncludeDir\fR "/path/" +Looks for files with .conf or .cfg extensions in "path", and includes all files, in sorted +order, inline in the configuration as if it were part of the configuration file. +This directive can be used in any block... but the result must be syntactically correct. +.TP .SH "HTTP Listener" An HTTP listener defines an address and port that .B Pound diff --git a/pound.h b/pound.h index b24da89..e576973 100644 --- a/pound.h +++ b/pound.h @@ -29,6 +29,7 @@ #include #include +#include #if HAVE_STDLIB_H #include #else From 3c4fb160721732dcc779d2e116f5f8c03ba095c7 Mon Sep 17 00:00:00 2001 From: Joe Gooch Date: Tue, 2 Feb 2010 08:57:35 -0500 Subject: [PATCH 11/21] Implement ForceHTTP10 Directive and SSLUncleanShutdown Directive ForceHTTP10 will force the downgrading of HTTP/1.1 connections to 1.0 connections based on the UserAgent string. Similar to BrowserMatch and SetEnvIf from Apache. However, this allows forcing non-ssl connections, as well as SSL connections (noHTTPS11) and do it based on the browser regexes, so we can be more specific than .*MSIE.* SSLUncleanShutdown matches against the user agent, and if it matches, does the mod_ssl unclean shutdown behavior. Compliant behavior is now the default. --- config.c | 33 +++++++++++++++++++++++++++++++++ http.c | 25 ++++++++++++++++++++++++- pound.8 | 15 +++++++++++++++ pound.h | 2 ++ 4 files changed, 74 insertions(+), 1 deletion(-) diff --git a/config.c b/config.c index 7711e44..eca8067 100644 --- a/config.c +++ b/config.c @@ -87,6 +87,8 @@ static regex_t ThreadModel; static regex_t IncludeDir; +static regex_t ForceHTTP10, SSLUncleanShutdown; + static regmatch_t matches[5]; static DH *DHCustom_params; @@ -922,6 +924,15 @@ parse_HTTP(void) res->rewr_dest = atoi(lin + matches[1].rm_so); } else if(!regexec(&LogLevel, lin, 4, matches, 0)) { res->log_level = atoi(lin + matches[1].rm_so); + } else if(!regexec(&ForceHTTP10, lin, 4, matches, 0)) { + if((m = (MATCHER *)malloc(sizeof(MATCHER))) == NULL) + conf_err("out of memory"); + memset(m, 0, sizeof(MATCHER)); + m->next = res->forcehttp10; + res->forcehttp10 = m; + lin[matches[1].rm_eo] = '\0'; + if(regcomp(&m->pat, lin + matches[1].rm_so, REG_ICASE | REG_NEWLINE | REG_EXTENDED)) + conf_err("ForceHTTP10 bad pattern"); } else if(!regexec(&Service, lin, 4, matches, 0)) { if(res->services == NULL) res->services = parse_service(NULL); @@ -1321,6 +1332,24 @@ parse_HTTPS(void) #endif } else if(!regexec(&NoHTTPS11, lin, 4, matches, 0)) { res->noHTTPS11 = atoi(lin + matches[1].rm_so); + } else if(!regexec(&ForceHTTP10, lin, 4, matches, 0)) { + if((m = (MATCHER *)malloc(sizeof(MATCHER))) == NULL) + conf_err("out of memory"); + memset(m, 0, sizeof(MATCHER)); + m->next = res->forcehttp10; + res->forcehttp10 = m; + lin[matches[1].rm_eo] = '\0'; + if(regcomp(&m->pat, lin + matches[1].rm_so, REG_ICASE | REG_NEWLINE | REG_EXTENDED)) + conf_err("bad pattern"); + } else if(!regexec(&SSLUncleanShutdown, lin, 4, matches, 0)) { + if((m = (MATCHER *)malloc(sizeof(MATCHER))) == NULL) + conf_err("out of memory"); + memset(m, 0, sizeof(MATCHER)); + m->next = res->ssl_uncln_shutdn; + res->ssl_uncln_shutdn = m; + lin[matches[1].rm_eo] = '\0'; + if(regcomp(&m->pat, lin + matches[1].rm_so, REG_ICASE | REG_NEWLINE | REG_EXTENDED)) + conf_err("bad pattern"); } else if(!regexec(&Service, lin, 4, matches, 0)) { if(res->services == NULL) res->services = parse_service(NULL); @@ -1616,6 +1645,8 @@ config_parse(const int argc, char **const argv) || regcomp(&VerifyList, "^[ \t]*VerifyList[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&CRLlist, "^[ \t]*CRLlist[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&NoHTTPS11, "^[ \t]*NoHTTPS11[ \t]+([0-2])[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) + || regcomp(&ForceHTTP10, "^[ \t]*ForceHTTP10[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) + || regcomp(&SSLUncleanShutdown, "^[ \t]*SSLUncleanShutdown[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&Include, "^[ \t]*Include[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&IncludeDir, "^[ \t]*IncludeDir[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&ConnTO, "^[ \t]*ConnTO[ \t]+([1-9][0-9]*)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) @@ -1791,6 +1822,8 @@ config_parse(const int argc, char **const argv) regfree(&VerifyList); regfree(&CRLlist); regfree(&NoHTTPS11); + regfree(&ForceHTTP10); + regfree(&SSLUncleanShutdown); regfree(&Include); regfree(&IncludeDir); regfree(&ConnTO); diff --git a/http.c b/http.c index 2515508..1dda3ae 100644 --- a/http.c +++ b/http.c @@ -1306,6 +1306,15 @@ do_http(thr_arg *arg) break; default: force_10 = 0; + if (lstn->forcehttp10) { + MATCHER *m; + + for(m = lstn->forcehttp10; m; m = m->next) + if(!regexec(&m->pat, u_agent, 0, NULL, 0)) { + force_10 = 1; + break; + } + } break; } @@ -1649,7 +1658,21 @@ do_http(thr_arg *arg) /* * This may help with some versions of IE with a broken channel shutdown */ - if(ssl != NULL) + if(lstn && lstn->ssl_uncln_shutdn && ssl != NULL) { + MATCHER *m; + int found = 0; + + for(m = lstn->ssl_uncln_shutdn; m; m = m->next) + if(!regexec(&m->pat, u_agent, 0, NULL, 0)) { + found = 1; + break; + } + + if(found) + SSL_set_shutdown(ssl, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN); + else + SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN); + } else if(ssl != NULL) SSL_set_shutdown(ssl, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN); clean_all(); diff --git a/pound.8 b/pound.8 index a6be176..8b57ff5 100644 --- a/pound.8 +++ b/pound.8 @@ -492,6 +492,12 @@ Override the global .I LogLevel value. .TP +\fBForceHTTP10\fR "user agent pattern" +Force connections for browser user agents matching this pattern to use +HTTP/1.0 even if the browser has requested HTTP/1.1. Some MSIE browsers have +problems with HTTP/1.1 over SSL connections. Other browsers, like Java/1.0 or +JDK connections might require this feature as well. +.TP \fBService\fR [ "name" ] This defines a private service (see below for service definition syntax). This service will be used only by this listener. The service may be optionally @@ -597,6 +603,15 @@ Behave like an HTTP/1.0 server for HTTPS clients. If this value is requests on SSL connections. If the value is 2 (default) disable multiple requests on SSL connections only for MSIE clients. Required work-around for a bug in certain versions of IE. +.TP +\fBSSLUncleanShutdown\fR "UserAgent Match Pattern" +Implement a workaround for MSIE's ssl shutdown code... basically don't +send a shutdown message because MSIE can't handle it. Pound, by default, +would do this on all connections, and will do that if this directive is +never specified. If this directive is specified, one or many times, any +matching useragent pattern will cause the ssl unclean shutdown behavior. +Other user agents will follow the mod_ssl compliant and safe code. +You likely want to set this for all MSIE browsers. .SH "Service" A service is a definition of which back-end servers .B Pound diff --git a/pound.h b/pound.h index e576973..c92736e 100644 --- a/pound.h +++ b/pound.h @@ -398,6 +398,8 @@ typedef struct _listener { POUND_CTX *ctx; /* CTX for SSL connections */ int clnt_check; /* client verification mode */ int noHTTPS11; /* HTTP 1.1 mode for SSL */ + MATCHER *forcehttp10; /* User Agent Patterns to force HTTP 1.0 mode */ + MATCHER *ssl_uncln_shutdn; /* User Agent Patterns to enable ssl unclean shutdown */ char *add_head; /* extra SSL header */ regex_t verb; /* pattern to match the request verb against */ int to; /* client time-out */ From 08d35c4b5c6de60bb0bb6ef5e192d468ad91d21a Mon Sep 17 00:00:00 2001 From: Joe Gooch Date: Mon, 6 Feb 2012 14:44:51 -0500 Subject: [PATCH 12/21] BackendCookie implementation Each Backend has a BackendKey which uniquely identifies the backend. When BackendCookies are enabled, pound will inject a cookie into the response that will tag the browser to the specified backend. In this way session affinity is handled in pound, without any need for session support on the backend servers, and sessions will persist across pound restarts because they're stored on the browser side. --- config.c | 46 +++++++++++++++++++++++++++++++ http.c | 35 ++++++++++++++++++++++++ pound.8 | 12 +++++++++ pound.h | 6 +++++ poundctl.c | 13 ++++++--- svc.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++++------ 6 files changed, 180 insertions(+), 11 deletions(-) diff --git a/config.c b/config.c index eca8067..c8a1bc1 100644 --- a/config.c +++ b/config.c @@ -89,6 +89,8 @@ static regex_t IncludeDir; static regex_t ForceHTTP10, SSLUncleanShutdown; +static regex_t BackendKey, BackendCookie; + static regmatch_t matches[5]; static DH *DHCustom_params; @@ -300,6 +302,7 @@ static BACKEND * parse_be(const int is_emergency) { char lin[MAXBUF]; + char *cp; BACKEND *res; int has_addr, has_port; struct hostent *host; @@ -318,6 +321,7 @@ parse_be(const int is_emergency) res->priority = 5; memset(&res->ha_addr, 0, sizeof(res->ha_addr)); res->url = NULL; + res->bekey = NULL; res->next = NULL; has_addr = has_port = 0; pthread_mutex_init(&res->mut, NULL); @@ -357,6 +361,10 @@ parse_be(const int is_emergency) conf_err("Port is supported only for INET/INET6 back-ends"); } has_port = 1; + } else if(!regexec(&BackendKey, lin, 4, matches, 0)) { + lin[matches[1].rm_eo] = '\0'; + if ((res->bekey = strdup(lin + matches[1].rm_so))==NULL) + conf_err("out of memory"); } else if(!regexec(&Priority, lin, 4, matches, 0)) { if(is_emergency) conf_err("Priority is not supported for Emergency back-ends"); @@ -488,6 +496,18 @@ parse_be(const int is_emergency) conf_err("BackEnd missing Address - aborted"); if((res->addr.ai_family == AF_INET || res->addr.ai_family == AF_INET6) && !has_port) conf_err("BackEnd missing Port - aborted"); + if(!res->bekey) { + if (res->addr.ai_family == AF_INET) + snprintf(lin, MAXBUF-1, "4-%08x-%x",htonl(((struct sockaddr_in *)(res->addr.ai_addr))->sin_addr.s_addr), htons(((struct sockaddr_in *)(res->addr.ai_addr))->sin_port)); + else if (res->addr.ai_family == AF_INET6) { + cp = (char*) &(((struct sockaddr_in6 *)(res->addr.ai_addr))->sin6_addr); + snprintf(lin, MAXBUF-1, "6-%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x-%x",cp[0],cp[1],cp[2],cp[3],cp[4],cp[5],cp[6],cp[7],cp[8],cp[9],cp[10],cp[11],cp[12],cp[13],cp[14],cp[15],htons(((struct sockaddr_in6 *)(res->addr.ai_addr))->sin6_port)); + } else + conf_err("cannot autogenerate backendkey, please specify one"); + + if ((res->bekey = strdup(lin))==NULL) + conf_err("out of memory autogenerating backendkey"); + } return res; } else { conf_err("unknown directive"); @@ -628,6 +648,7 @@ static SERVICE * parse_service(const char *svc_name) { char lin[MAXBUF]; + char pat[MAXBUF]; SERVICE *res; BACKEND *be; MATCHER *m; @@ -759,6 +780,27 @@ parse_service(const char *svc_name) res->backends = parse_be(0); } else if(!regexec(&Emergency, lin, 4, matches, 0)) { res->emergency = parse_be(1); + } else if(!regexec(&BackendCookie, lin, 5, matches, 0)) { + lin[matches[1].rm_eo] = '\0'; + lin[matches[2].rm_eo] = '\0'; + lin[matches[3].rm_eo] = '\0'; + lin[matches[4].rm_eo] = '\0'; + snprintf(pat, MAXBUF - 1, "Cookie[^:]*:.*[; \t]%s=\"?([^\";]*)\"?", lin + matches[1].rm_so); + if(matches[1].rm_so==matches[1].rm_eo) + conf_err("Backend cookie must have a name"); + if((res->becookie=strdup(lin+matches[1].rm_so))==NULL) + conf_err("out of memory"); + if(regcomp(&res->becookie_re, pat, REG_ICASE | REG_NEWLINE | REG_EXTENDED)) + conf_err("Backend Cookie pattern failed - aborted"); + if(matches[2].rm_so!=matches[2].rm_eo && (res->becdomain=strdup(lin+matches[2].rm_so))==NULL) + conf_err("out of memory"); + if(matches[3].rm_so!=matches[3].rm_eo && (res->becpath=strdup(lin+matches[3].rm_so))==NULL) + conf_err("out of memory"); + res->becage = atoi(lin+matches[4].rm_so); + if ((lin[matches[4].rm_so]&~0x20)=='S') + res->becage = -1; + else + res->becage = atoi(lin+matches[4].rm_so); } else if(!regexec(&Session, lin, 4, matches, 0)) { parse_sess(res); } else if(!regexec(&IgnoreCase, lin, 4, matches, 0)) { @@ -1602,6 +1644,7 @@ config_parse(const int argc, char **const argv) || regcomp(&ListenHTTP, "^[ \t]*ListenHTTP[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&ListenHTTPS, "^[ \t]*ListenHTTPS[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&End, "^[ \t]*End[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) + || regcomp(&BackendKey, "^[ \t]*Key[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&Address, "^[ \t]*Address[ \t]+([^ \t]+)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&Port, "^[ \t]*Port[ \t]+([1-9][0-9]*)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&Cert, "^[ \t]*Cert[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) @@ -1621,6 +1664,7 @@ config_parse(const int argc, char **const argv) || regcomp(&Service, "^[ \t]*Service[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&ServiceName, "^[ \t]*Service[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&URL, "^[ \t]*URL[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) + || regcomp(&BackendCookie, "^[ \t]*BackendCookie[ \t]+\"(.+)\"[ \t]+\"(.*)\"[ \t]+\"(.*)\"[ \t]+([0-9]+|Session)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&HeadRequire, "^[ \t]*HeadRequire[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&HeadDeny, "^[ \t]*HeadDeny[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&BackEnd, "^[ \t]*BackEnd[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) @@ -1779,6 +1823,7 @@ config_parse(const int argc, char **const argv) regfree(&ListenHTTP); regfree(&ListenHTTPS); regfree(&End); + regfree(&BackendKey); regfree(&Address); regfree(&Port); regfree(&Cert); @@ -1798,6 +1843,7 @@ config_parse(const int argc, char **const argv) regfree(&Service); regfree(&ServiceName); regfree(&URL); + regfree(&BackendCookie); regfree(&HeadRequire); regfree(&HeadDeny); regfree(&BackEnd); diff --git a/http.c b/http.c index 1dda3ae..70ea0b7 100644 --- a/http.c +++ b/http.c @@ -556,6 +556,8 @@ do_http(thr_arg *arg) LONG cont, res_bytes; regmatch_t matches[4]; struct linger l; + struct tm expires; + time_t exptime; double start_req, end_req; RENEG_STATE reneg_state; BIO_ARG ba1, ba2; @@ -1489,6 +1491,39 @@ do_http(thr_arg *arg) } } free_headers(headers); + if(!skip && cur_backend->be_type == 0 && svc->becookie && cur_backend->bekey) { + char *cp = buf; + char *ep = buf + sizeof(buf) - 1; + buf[0] = '\0'; + snprintf(cp, (ep-cp-1), "Set-Cookie: %s=%s", svc->becookie, cur_backend->bekey); + cp += strlen(cp); + if(svc->becage!=0) { + strncat(cp, "; expires=", ep-cp-1); + cp += strlen(cp); + exptime = time(NULL); + /* Explicit age? Or match session timer? (-1) */ + if (svc->becage>0) + exptime += svc->becage; + else + exptime += svc->sess_ttl; + strftime(cp, ep-cp-1, "%a, %e-%b-%Y %H:%M:%S GMT", gmtime(&exptime)); + cp += strlen(cp); + } + if(svc->becpath) { + strncat(cp, "; path=", ep-cp-1); + cp += strlen(cp); + strncat(cp, svc->becpath, ep-cp-1); + cp += strlen(cp); + } + if(svc->becdomain) { + strncat(cp, "; domain=", ep-cp-1); + cp += strlen(cp); + strncat(cp, svc->becdomain, ep-cp-1); + cp += strlen(cp); + } + /*logmsg(LOG_DEBUG, "%s", buf);*/ + BIO_printf(cl, "%s\r\n", buf); + } /* final CRLF */ if(!skip) diff --git a/pound.8 b/pound.8 index 8b57ff5..61c07ae 100644 --- a/pound.8 +++ b/pound.8 @@ -381,6 +381,10 @@ will listen on. This is a .B mandatory parameter. .TP +\fBKey\fR "key" +The key associated to this backend, if using BackendCookie in the service. +If left blank, it'll be autogenerated from the backend address. +.TP \fBxHTTP\fR value Defines which HTTP verbs are accepted. The possible values are: .IP @@ -642,6 +646,14 @@ was defined then all requests match. The matching is by default case-sensitive, but this can be overridden by specifying .B IgnoreCase 1 .TP +\fBBackendCookie\fR "cookiename" "domain" "path" age|Session +If defined, Pound will inject a cookie in each response with the appropriate backend's key, so that +even if the session table is flushed or sessions are disabled, the proper backend can be chosen. +This allows for session databases to be offloaded to the client side via browser cookies. +See \fBKey\fR in the backend definition. The given age will be how many seconds the cookie will +persist for. If set to 0, it will be a so-called "memory" cookie which will expire when the browser +closes. If set to "Session", it will mimick the session TTL behavior. +.TP \fBIgnoreCase\fR 0|1 Override the global .B IgnoreCase diff --git a/pound.h b/pound.h index c92736e..e5d13ea 100644 --- a/pound.h +++ b/pound.h @@ -329,6 +329,7 @@ typedef struct _backend { struct addrinfo ha_addr; /* HA address/port */ char *url; /* for redirectors */ int redir_req; /* the redirect should include the request path */ + char *bekey; /* Backend Key for Cookie */ SSL_CTX *ctx; /* CTX for SSL connections */ pthread_mutex_t mut; /* mutex for this back-end */ int n_requests; /* number of requests seen */ @@ -375,6 +376,11 @@ typedef struct _service { #else LHASH *sessions; /* currently active sessions */ #endif + regex_t becookie_re;/* Regexs to find backend cookies */ + char *becookie, /* Backend Cookie Name */ + *becdomain, /* Backend Cookie domain */ + *becpath; /* Backend cookie path */ + int becage; /* Backend cookie age */ int disabled; /* true if the service is disabled */ struct _service *next; } SERVICE; diff --git a/poundctl.c b/poundctl.c index 2ceb8ea..45c3e94 100644 --- a/poundctl.c +++ b/poundctl.c @@ -102,12 +102,19 @@ be_prt(const int sock) { BACKEND be; struct sockaddr_storage a, h; - int n_be; + char bekey[MAXBUF+1]; + int n_be,sz; n_be = 0; while(read(sock, (void *)&be, sizeof(BACKEND)) == sizeof(BACKEND)) { if(be.disabled < 0) break; + + read(sock, &sz, sizeof(sz)); + if(sz) read(sock, bekey, sz); + bekey[sz]='\0'; + be.bekey=bekey; + read(sock, &a, be.addr.ai_addrlen); be.addr.ai_addr = (struct sockaddr *)&a; if(be.ha_addr.ai_addrlen > 0) { @@ -115,8 +122,8 @@ be_prt(const int sock) be.ha_addr.ai_addr = (struct sockaddr *)&h; } if(xml_out) - printf("\n", - n_be++, + printf("\n", + n_be++, be.bekey, prt_addr(&be.addr), be.t_average / 1000000, be.priority, be.alive? "yes": "DEAD", be.disabled? "DISABLED": "active"); else diff --git a/svc.c b/svc.c index 9f84042..7756019 100644 --- a/svc.c +++ b/svc.c @@ -527,6 +527,27 @@ get_HEADERS(char *res, const SERVICE *svc, char **const headers) return res[0] != '\0'; } +static int +get_bekey_from_HEADERS(char *res, const SERVICE *svc, char **const headers) +{ + int i, n, s; + regmatch_t matches[4]; + + res[0] = '\0'; + if (!svc->becookie) return res[0] != '\0'; + for(i = 0; i < (MAXHEADERS - 1); i++) { + if(headers[i] == NULL) + continue; + if(regexec(&svc->becookie_re, headers[i], 4, matches, 0)) + continue; + if((n = matches[1].rm_eo - matches[1].rm_so) > KEY_SIZE) + n = KEY_SIZE; + strncpy(res, headers[i] + matches[1].rm_so, n); + res[n] = '\0'; + } + return res[0] != '\0'; +} + /* * Pick a random back-end from a candidate list */ @@ -545,6 +566,17 @@ rand_backend(BACKEND *be, int pri) return be; } +static BACKEND * +get_backend_by_key(BACKEND *be, const char *bekey) { + if (!bekey || !*bekey) return NULL; + while(be) { + if(be->bekey && strcmp(be->bekey, bekey)==0) return be; + be = be->next; + } + return NULL; +} + + /* * return a back-end based on a fixed hash value * this is used for session_ttl < 0 @@ -588,6 +620,7 @@ get_backend(SERVICE *const svc, const struct addrinfo *from_host, const char *re { BACKEND *res; char key[KEY_SIZE + 1]; + char bekey[KEY_SIZE + 1]; int ret_val, no_be; void *vp; @@ -596,21 +629,32 @@ get_backend(SERVICE *const svc, const struct addrinfo *from_host, const char *re no_be = (svc->tot_pri <= 0); + key[0]='\0'; + bekey[0]='\0'; switch(svc->sess_type) { case SESS_NONE: /* choose one back-end randomly */ - res = no_be? svc->emergency: rand_backend(svc->backends, random() % svc->tot_pri); + if (no_be) res = svc->emergency; + else if (get_bekey_from_HEADERS(bekey, svc, headers)) { + logmsg(LOG_DEBUG, "Found BEKEY %s in headers",bekey); + res = get_backend_by_key(svc->backends, bekey); + if (res==NULL || !res->alive ) res = rand_backend(svc->backends, random() % svc->tot_pri); else logmsg(LOG_DEBUG, "found matching backend by bekey"); + } else res = rand_backend(svc->backends, random() % svc->tot_pri); break; case SESS_IP: addr2str(key, KEY_SIZE, from_host, 1); - if(svc->sess_ttl < 0) + if(svc->sess_ttl < 0) { res = no_be? svc->emergency: hash_backend(svc->backends, svc->abs_pri, key); - else if((vp = t_find(svc->sessions, key)) == NULL) { + } else if((vp = t_find(svc->sessions, key)) == NULL) { if(no_be) res = svc->emergency; else { /* no session yet - create one */ - res = rand_backend(svc->backends, random() % svc->tot_pri); + if (get_bekey_from_HEADERS(bekey, svc, headers)) { + logmsg(LOG_DEBUG, "Found BEKEY %s in headers",bekey); + res = get_backend_by_key(svc->backends, bekey); + if (res==NULL || !res->alive ) res = rand_backend(svc->backends, random() % svc->tot_pri); else logmsg(LOG_DEBUG, "found matching backend by bekey"); + } else res = rand_backend(svc->backends, random() % svc->tot_pri); t_add(svc->sessions, key, &res, sizeof(res)); } } else @@ -626,7 +670,11 @@ get_backend(SERVICE *const svc, const struct addrinfo *from_host, const char *re res = svc->emergency; else { /* no session yet - create one */ - res = rand_backend(svc->backends, random() % svc->tot_pri); + if (get_bekey_from_HEADERS(bekey, svc, headers)) { + logmsg(LOG_DEBUG,"Found BEKEY %s in headers",bekey); + res = get_backend_by_key(svc->backends, bekey); + if (res==NULL || !res->alive ) res = rand_backend(svc->backends, random() % svc->tot_pri); else logmsg(LOG_DEBUG, "found matching backend by bekey"); + } else res = rand_backend(svc->backends, random() % svc->tot_pri); t_add(svc->sessions, key, &res, sizeof(res)); } } else @@ -645,13 +693,22 @@ get_backend(SERVICE *const svc, const struct addrinfo *from_host, const char *re res = svc->emergency; else { /* no session yet - create one */ - res = rand_backend(svc->backends, random() % svc->tot_pri); + if (get_bekey_from_HEADERS(bekey, svc, headers)) { + logmsg(LOG_DEBUG, "Found BEKEY %s in headers",bekey); + res = get_backend_by_key(svc->backends, bekey); + if (res==NULL || !res->alive ) res = rand_backend(svc->backends, random() % svc->tot_pri); else printf("found matching backend by bekey"); + } else res = rand_backend(svc->backends, random() % svc->tot_pri); t_add(svc->sessions, key, &res, sizeof(res)); } } else memcpy(&res, vp, sizeof(res)); } else { - res = no_be? svc->emergency: rand_backend(svc->backends, random() % svc->tot_pri); + if (no_be) res = svc->emergency; + else if (get_bekey_from_HEADERS(bekey, svc, headers)) { + logmsg(LOG_DEBUG, "Found BEKEY %s in headers",bekey); + res = get_backend_by_key(svc->backends, bekey); + if (res==NULL || !res->alive ) res = rand_backend(svc->backends, random() % svc->tot_pri); else logmsg(LOG_DEBUG, "found matching backend by bekey"); + } else res = rand_backend(svc->backends, random() % svc->tot_pri); } break; } @@ -1505,7 +1562,7 @@ thr_control(void *arg) { CTRL_CMD cmd; struct sockaddr sa; - int ctl, dummy, n, ret_val; + int ctl, dummy, n, ret_val, sz; LISTENER *lstn, dummy_lstn; SERVICE *svc, dummy_svc; BACKEND *be, dummy_be; @@ -1552,6 +1609,9 @@ thr_control(void *arg) (void)write(ctl, (void *)svc, sizeof(SERVICE)); for(be = svc->backends; be; be = be->next) { (void)write(ctl, (void *)be, sizeof(BACKEND)); + sz = be->bekey?strlen(be->bekey):0; + (void)write(ctl, (void *)&sz, sizeof(sz)); + if(sz>0) (void)write(ctl, (void *)be->bekey, sz); (void)write(ctl, be->addr.ai_addr, be->addr.ai_addrlen); if(be->ha_addr.ai_addrlen > 0) (void)write(ctl, be->ha_addr.ai_addr, be->ha_addr.ai_addrlen); @@ -1573,6 +1633,9 @@ thr_control(void *arg) (void)write(ctl, (void *)svc, sizeof(SERVICE)); for(be = svc->backends; be; be = be->next) { (void)write(ctl, (void *)be, sizeof(BACKEND)); + sz = be->bekey?strlen(be->bekey):0; + (void)write(ctl, (void *)&sz, sizeof(sz)); + if(sz>0) (void)write(ctl, (void *)be->bekey, sz); (void)write(ctl, be->addr.ai_addr, be->addr.ai_addrlen); if(be->ha_addr.ai_addrlen > 0) (void)write(ctl, be->ha_addr.ai_addr, be->ha_addr.ai_addrlen); From ab0fbaab5bd4c947e359be6b1895fd73f59725ca Mon Sep 17 00:00:00 2001 From: Joe Gooch Date: Mon, 20 Oct 2014 11:04:58 -0400 Subject: [PATCH 13/21] Implement PCRE based Dynamic Redirect support Redirects can now be specified as Redirect, RedirectAppend or RedirectDynamic. --- config.c | 50 +++++++++++++++++--------------------------------- http.c | 37 +++++++++++++++++++++++++++++++++++-- pound.8 | 37 +++++++++++++++++++++++++++---------- pound.h | 2 +- 4 files changed, 80 insertions(+), 46 deletions(-) diff --git a/config.c b/config.c index c8a1bc1..d1bc0ab 100644 --- a/config.c +++ b/config.c @@ -77,7 +77,7 @@ static regex_t Empty, Comment, User, Group, RootJail, Daemon, LogFacility, LogL static regex_t ListenHTTP, ListenHTTPS, End, Address, Port, Cert, xHTTP, Client, CheckURL; static regex_t Err414, Err500, Err501, Err503, ErrNoSsl, NoSslRedirect, MaxRequest, HeadRemove, RewriteLocation, RewriteDestination; static regex_t Service, ServiceName, URL, HeadRequire, HeadDeny, BackEnd, Emergency, Priority, HAport, HAportAddr; -static regex_t Redirect, RedirectN, TimeOut, Session, Type, TTL, ID; +static regex_t Redirect, TimeOut, Session, Type, TTL, ID; static regex_t ClientCert, AddHeader, DisableProto, SSLAllowClientRenegotiation, SSLHonorCipherOrder, Ciphers; static regex_t CAlist, VerifyList, CRLlist, NoHTTPS11, Grace, Include, ConnTO, IgnoreCase, HTTPS; static regex_t Disabled, Threads, CNName, Anonymise, DHParams, ECDHCurve; @@ -731,44 +731,30 @@ parse_service(const char *svc_name) conf_err("Redirect config: out of memory - aborted"); be = res->backends; } + // 1 - Dynamic or not, 2 - Request Redirect #, 3 - Destination URL memset(be, 0, sizeof(BACKEND)); be->be_type = 302; - be->priority = 1; - be->alive = 1; - pthread_mutex_init(& be->mut, NULL); - lin[matches[1].rm_eo] = '\0'; - if((be->url = strdup(lin + matches[1].rm_so)) == NULL) - conf_err("Redirector config: out of memory - aborted"); - /* split the URL into its fields */ - if(regexec(&LOCATION, be->url, 4, matches, 0)) - conf_err("Redirect bad URL - aborted"); - if((be->redir_req = matches[3].rm_eo - matches[3].rm_so) == 1) - /* the path is a single '/', so remove it */ - be->url[matches[3].rm_so] = '\0'; - } else if(!regexec(&RedirectN, lin, 4, matches, 0)) { - if(res->backends) { - for(be = res->backends; be->next; be = be->next) - ; - if((be->next = (BACKEND *)malloc(sizeof(BACKEND))) == NULL) - conf_err("Redirect config: out of memory - aborted"); - be = be->next; - } else { - if((res->backends = (BACKEND *)malloc(sizeof(BACKEND))) == NULL) - conf_err("Redirect config: out of memory - aborted"); - be = res->backends; + be->redir_req = 0; + if (matches[1].rm_eo != matches[1].rm_so) { + if((lin[matches[1].rm_so] & ~0x20)=='D') { + be->redir_req = 2; + if(!res->url || res->url->next) + conf_err("Dynamic Redirect must be preceeded by a URL line"); + } else if((lin[matches[1].rm_so] & ~0x20)=='A') + be->redir_req = 1; } - memset(be, 0, sizeof(BACKEND)); - be->be_type = atoi(lin + matches[1].rm_so); + if (matches[2].rm_eo != matches[2].rm_so) + be->be_type = atoi(lin + matches[2].rm_so); be->priority = 1; be->alive = 1; - pthread_mutex_init(& be->mut, NULL); - lin[matches[2].rm_eo] = '\0'; - if((be->url = strdup(lin + matches[2].rm_so)) == NULL) + pthread_mutex_init(&be->mut, NULL); + lin[matches[3].rm_eo] = '\0'; + if((be->url = strdup(lin + matches[3].rm_so)) == NULL) conf_err("Redirector config: out of memory - aborted"); /* split the URL into its fields */ if(regexec(&LOCATION, be->url, 4, matches, 0)) conf_err("Redirect bad URL - aborted"); - if((be->redir_req = matches[3].rm_eo - matches[3].rm_so) == 1) + if((matches[3].rm_eo - matches[3].rm_so) == 1) /* the path is a single '/', so remove it */ be->url[matches[3].rm_so] = '\0'; } else if(!regexec(&BackEnd, lin, 4, matches, 0)) { @@ -1673,8 +1659,7 @@ config_parse(const int argc, char **const argv) || regcomp(&TimeOut, "^[ \t]*TimeOut[ \t]+([1-9][0-9]*)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&HAport, "^[ \t]*HAport[ \t]+([1-9][0-9]*)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&HAportAddr, "^[ \t]*HAport[ \t]+([^ \t]+)[ \t]+([1-9][0-9]*)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) - || regcomp(&Redirect, "^[ \t]*Redirect[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) - || regcomp(&RedirectN, "^[ \t]*Redirect[ \t]+(30[127])[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) + || regcomp(&Redirect, "^[ \t]*Redirect(Append|Dynamic|)[ \t]+(30[127][ \t]+|)\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&Session, "^[ \t]*Session[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&Type, "^[ \t]*Type[ \t]+([^ \t]+)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&TTL, "^[ \t]*TTL[ \t]+([1-9-][0-9]*)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) @@ -1853,7 +1838,6 @@ config_parse(const int argc, char **const argv) regfree(&HAport); regfree(&HAportAddr); regfree(&Redirect); - regfree(&RedirectN); regfree(&Session); regfree(&Type); regfree(&TTL); diff --git a/http.c b/http.c index 70ea0b7..ff4feac 100644 --- a/http.c +++ b/http.c @@ -1324,9 +1324,38 @@ do_http(thr_arg *arg) if(cur_backend->be_type) { memset(buf, 0, sizeof(buf)); if(!cur_backend->redir_req) - snprintf(buf, sizeof(buf) - 1, "%s%s", cur_backend->url, url); - else strncpy(buf, cur_backend->url, sizeof(buf) - 1); + else if (cur_backend->redir_req==1) + snprintf(buf, sizeof(buf) - 1, "%s%s", cur_backend->url, url); + else { + regmatch_t umtch[10]; + char *chptr, *enptr, *srcptr; + + // Redirect Dynamic + //fprintf(stderr, "redir dynamic url %s replace %s\n", url, cur_backend->url); + + if(regexec(&svc->url->pat, url, 10, umtch, 0)) + logmsg(LOG_WARNING, "URL pattern didn't match in redirdynamic... shouldn't happen %s", url); + chptr = buf; + enptr = buf + sizeof(buf) - 1; + *enptr = '\0'; + srcptr = cur_backend->url; + for(; *srcptr && chptr < enptr-1; ) { + if (srcptr[0] == '$' && srcptr[1] == '$') { + *chptr++ = *srcptr++; + srcptr++; + } + if (srcptr[0] == '$' && isdigit(srcptr[1])) { + if (chptr + umtch[srcptr[1]-0x30].rm_eo - umtch[srcptr[1]-0x30].rm_so > enptr-1) break; + memcpy(chptr, url + umtch[srcptr[1]-0x30].rm_so, umtch[srcptr[1]-0x30].rm_eo - umtch[srcptr[1]-0x30].rm_so); + chptr += umtch[srcptr[1]-0x30].rm_eo - umtch[srcptr[1]-0x30].rm_so; + srcptr += 2; + continue; + } + *chptr++ = *srcptr++; + } + *chptr++='\0'; + } redirect_reply(cl, buf, cur_backend->be_type); addr2str(caddr, MAXBUF - 1, &from_host, 1); switch(lstn->log_level) { @@ -1349,6 +1378,10 @@ do_http(thr_arg *arg) logmsg(LOG_INFO, "%s - %s [%s] \"%s\" %d 0 \"%s\" \"%s\"", caddr, u_name[0]? u_name: "-", req_time, request, cur_backend->be_type, referer, u_agent); break; + case 6: + logmsg(LOG_INFO, "%s - %s [%s] \"%s\" %d 0 \"%s\" \"%s\" \"%s\"", caddr, + u_name[0]? u_name: "-", req_time, request, cur_backend->be_type, buf, referer, u_agent); + break; } if(!cl_11 || conn_closed || force_10) break; diff --git a/pound.8 b/pound.8 index 61c07ae..0d6e5ce 100644 --- a/pound.8 +++ b/pound.8 @@ -699,19 +699,25 @@ multiple back-ends per service, in which case .B Pound will attempt to load-balance between them. .TP -\fBRedirect\fR [code] "url" +\fB[Redirect | RedirectAppend | RedirectDynamic]\fR [code] "url" This is a special type of back-end. Instead of sending the request to a back-end .B Pound replies immediately with a redirection to the given URL. You may define multiple redirectors in a service, as well as mixing them with regular back-ends. .IP -The address the client is redirected to is determined by the actual +The address the client is redirected to is determined by the command you specify. +If you specify \fBRedirect\fR, the url is taken as an absolute host and path +to redirect to. If you use \fBRedirectAppend\fR, the original request path +will be appended to the host and path you specified. If you use \fBRedirectDynamic\fR, +then .I url -you specify: if it is a "pure" host (i.e. with no path) then the client will be -redirected to the host you specified, with the original request path appended. If -your -.I url -does contain a path then the request path is ignored. +can contain RegEx replacements in the form +.I $1 +through +.I $9 +which indicate expression captured from the original request path. You must have a +\fBURL\fR directive, and the first \fBURL\fR directive for the service is the one +used for capturing expressions. .IP Examples: if you specified .br @@ -724,17 +730,28 @@ Examples: if you specified and the client requested .I http://xyz/a/b/c then it will be redirected to -.IR "http://abc.example/a/b/c", +.IR "http://abc.example", but if you specified .br .br - Redirect "http://abc.example/index.html" + RedirectAppend "http://abc.example" +.br + +.br +it will be sent to +.IR "http://abc.example/a/b/c. +.IP +If you specified +.br + URL "^/a(/([^/]*)(/[^/]*)" +.br + RedirectDynamic "http://abc.example$2$1/index.html" .br .br it will be sent to -.IR "http://abc.example/index.html". +.IR "http://abc.example/c/b/index.html. .IP .IR "Technical note": in an ideal world diff --git a/pound.h b/pound.h index e5d13ea..ae490c4 100644 --- a/pound.h +++ b/pound.h @@ -328,7 +328,7 @@ typedef struct _backend { int conn_to; /* connection time-out */ struct addrinfo ha_addr; /* HA address/port */ char *url; /* for redirectors */ - int redir_req; /* the redirect should include the request path */ + int redir_req; /* 0 - redirect is absolute, 1 - the redirect should include the request path, or 2 if it should use perl dynamic replacement */ char *bekey; /* Backend Key for Cookie */ SSL_CTX *ctx; /* CTX for SSL connections */ pthread_mutex_t mut; /* mutex for this back-end */ From a4fb59ff22390990f5abba0229acbaa102650421 Mon Sep 17 00:00:00 2001 From: Joe Gooch Date: Thu, 13 Dec 2012 14:48:43 -0500 Subject: [PATCH 14/21] CertDir directive for loading certificates from a folder --- config.c | 199 +++++++++++++++++++++++++++++++++++++------------------ pound.8 | 43 +++++++++++- 2 files changed, 175 insertions(+), 67 deletions(-) diff --git a/config.c b/config.c index d1bc0ab..3778b42 100644 --- a/config.c +++ b/config.c @@ -74,7 +74,7 @@ static CODE facilitynames[] = { #endif static regex_t Empty, Comment, User, Group, RootJail, Daemon, LogFacility, LogLevel, Alive, SSLEngine, Control; -static regex_t ListenHTTP, ListenHTTPS, End, Address, Port, Cert, xHTTP, Client, CheckURL; +static regex_t ListenHTTP, ListenHTTPS, End, Address, Port, Cert, CertDir, xHTTP, Client, CheckURL; static regex_t Err414, Err500, Err501, Err503, ErrNoSsl, NoSslRedirect, MaxRequest, HeadRemove, RewriteLocation, RewriteDestination; static regex_t Service, ServiceName, URL, HeadRequire, HeadDeny, BackEnd, Emergency, Priority, HAport, HAportAddr; static regex_t Redirect, TimeOut, Session, Type, TTL, ID; @@ -122,6 +122,9 @@ static char *f_name[MAX_FIN]; static int n_lin[MAX_FIN]; static int cur_fin; +static void load_cert(int has_other, LISTENER *res, char *filename); +static void load_certdir(int has_other, LISTENER *res, const char *dir_path); + static int conf_init(const char *name) { @@ -1167,73 +1170,11 @@ parse_HTTPS(void) } else if(!regexec(&LogLevel, lin, 4, matches, 0)) { res->log_level = atoi(lin + matches[1].rm_so); } else if(!regexec(&Cert, lin, 4, matches, 0)) { -#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB - /* we have support for SNI */ - FILE *fcert; - char server_name[MAXBUF], *cp; - X509 *x509; - - if(has_other) - conf_err("Cert directives MUST precede other SSL-specific directives - aborted"); - if(res->ctx) { - for(pc = res->ctx; pc->next; pc = pc->next) - ; - if((pc->next = malloc(sizeof(POUND_CTX))) == NULL) - conf_err("ListenHTTPS new POUND_CTX: out of memory - aborted"); - pc = pc->next; - } else { - if((res->ctx = malloc(sizeof(POUND_CTX))) == NULL) - conf_err("ListenHTTPS new POUND_CTX: out of memory - aborted"); - pc = res->ctx; - } - if((pc->ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) - conf_err("SSL_CTX_new failed - aborted"); - pc->server_name = NULL; - pc->next = NULL; lin[matches[1].rm_eo] = '\0'; - if(SSL_CTX_use_certificate_chain_file(pc->ctx, lin + matches[1].rm_so) != 1) - conf_err("SSL_CTX_use_certificate_chain_file failed - aborted"); - if(SSL_CTX_use_PrivateKey_file(pc->ctx, lin + matches[1].rm_so, SSL_FILETYPE_PEM) != 1) - conf_err("SSL_CTX_use_PrivateKey_file failed - aborted"); - if(SSL_CTX_check_private_key(pc->ctx) != 1) - conf_err("SSL_CTX_check_private_key failed - aborted"); - if((fcert = fopen(lin + matches[1].rm_so, "r")) == NULL) - conf_err("ListenHTTPS: could not open certificate file"); - if((x509 = PEM_read_X509(fcert, NULL, NULL, NULL)) == NULL) - conf_err("ListenHTTPS: could not get certificate subject"); - fclose(fcert); - memset(server_name, '\0', MAXBUF); - X509_NAME_oneline(X509_get_subject_name(x509), server_name, MAXBUF - 1); - pc->subjectAltNameCount = 0; - pc->subjectAltNames = NULL; - pc->subjectAltNames = get_subjectaltnames(x509, &(pc->subjectAltNameCount)); - X509_free(x509); - if(!regexec(&CNName, server_name, 4, matches, 0)) { - server_name[matches[1].rm_eo] = '\0'; - if((pc->server_name = strdup(server_name + matches[1].rm_so)) == NULL) - conf_err("ListenHTTPS: could not set certificate subject"); - } else - conf_err("ListenHTTPS: could not get certificate CN"); -#else - /* no SNI support */ - if(has_other) - conf_err("Cert directives MUST precede other SSL-specific directives - aborted"); - if(res->ctx) - conf_err("ListenHTTPS: multiple certificates not supported - aborted"); - if((res->ctx = malloc(sizeof(POUND_CTX))) == NULL) - conf_err("ListenHTTPS new POUND_CTX: out of memory - aborted"); - res->ctx->server_name = NULL; - res->ctx->next = NULL; - if((res->ctx->ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) - conf_err("SSL_CTX_new failed - aborted"); + load_cert(has_other, res, lin+matches[1].rm_so); + } else if(!regexec(&CertDir, lin, 4, matches, 0)) { lin[matches[1].rm_eo] = '\0'; - if(SSL_CTX_use_certificate_chain_file(res->ctx->ctx, lin + matches[1].rm_so) != 1) - conf_err("SSL_CTX_use_certificate_chain_file failed - aborted"); - if(SSL_CTX_use_PrivateKey_file(res->ctx->ctx, lin + matches[1].rm_so, SSL_FILETYPE_PEM) != 1) - conf_err("SSL_CTX_use_PrivateKey_file failed - aborted"); - if(SSL_CTX_check_private_key(res->ctx->ctx) != 1) - conf_err("SSL_CTX_check_private_key failed - aborted"); -#endif + load_certdir(has_other, res, lin+matches[1].rm_so); } else if(!regexec(&ClientCert, lin, 4, matches, 0)) { has_other = 1; if(res->ctx == NULL) @@ -1442,6 +1383,130 @@ parse_HTTPS(void) return NULL; } +static void load_certdir(int has_other, LISTENER *res, const char *dir_path) { + DIR * dp; + struct dirent *de; + + char buf[512]; + char *files[200], *cp; + char *pattern; + int filecnt = 0; + int idx,use; + + logmsg(LOG_DEBUG, "Including Certs from Dir %s", dir_path); + + pattern = strrchr(dir_path, '/'); + if (pattern) { + *pattern++ = 0; + if (!*pattern) pattern = NULL; + } + + if((dp = opendir(dir_path)) == NULL) { + conf_err("can't open IncludeDir directory"); + exit(1); + } + + while((de = readdir(dp))!=NULL) { + if (de->d_name[0] == '.') continue; + if (!pattern || fnmatch(pattern, de->d_name, 0) == 0 ) { + snprintf(buf, sizeof(buf), "%s%s%s", dir_path, (dir_path[strlen(dir_path)-1]=='/')?"":"/", de->d_name); + buf[sizeof(buf)-1] = 0; + if (filecnt == sizeof(files)/sizeof(*files)) { + conf_err("Max certificate files per directory reached"); + } + if ((files[filecnt++] = strdup(buf)) == NULL) { + conf_err("CertDir out of memory"); + } + continue; + } + } + /* We order the list, and load in ascending order */ + while(filecnt) { + use = 0; + for(idx = 1; idx0) + use=idx; + + logmsg(LOG_DEBUG, " I Cert ==> %s", files[use]); + + load_cert(has_other, res, files[use]); + files[use] = files[--filecnt]; + } + + closedir(dp); +} + +static void +load_cert(int has_other, LISTENER *res, char *filename) +{ + POUND_CTX *pc; +#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB + /* we have support for SNI */ + FILE *fcert; + char server_name[MAXBUF], *cp; + X509 *x509; + + if(has_other) + conf_err("Cert directives MUST precede other SSL-specific directives - aborted"); + if(res->ctx) { + for(pc = res->ctx; pc->next; pc = pc->next) + ; + if((pc->next = malloc(sizeof(POUND_CTX))) == NULL) + conf_err("ListenHTTPS new POUND_CTX: out of memory - aborted"); + pc = pc->next; + } else { + if((res->ctx = malloc(sizeof(POUND_CTX))) == NULL) + conf_err("ListenHTTPS new POUND_CTX: out of memory - aborted"); + pc = res->ctx; + } + if((pc->ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) + conf_err("SSL_CTX_new failed - aborted"); + pc->server_name = NULL; + pc->next = NULL; + if(SSL_CTX_use_certificate_chain_file(pc->ctx, filename) != 1) + conf_err("SSL_CTX_use_certificate_chain_file failed - aborted"); + if(SSL_CTX_use_PrivateKey_file(pc->ctx, filename, SSL_FILETYPE_PEM) != 1) + conf_err("SSL_CTX_use_PrivateKey_file failed - aborted"); + if(SSL_CTX_check_private_key(pc->ctx) != 1) + conf_err("SSL_CTX_check_private_key failed - aborted"); + if((fcert = fopen(filename, "r")) == NULL) + conf_err("ListenHTTPS: could not open certificate file"); + if((x509 = PEM_read_X509(fcert, NULL, NULL, NULL)) == NULL) + conf_err("ListenHTTPS: could not get certificate subject"); + fclose(fcert); + memset(server_name, '\0', MAXBUF); + X509_NAME_oneline(X509_get_subject_name(x509), server_name, MAXBUF - 1); + pc->subjectAltNameCount = 0; + pc->subjectAltNames = NULL; + pc->subjectAltNames = get_subjectaltnames(x509, &(pc->subjectAltNameCount)); + X509_free(x509); + if(!regexec(&CNName, server_name, 4, matches, 0)) { + server_name[matches[1].rm_eo] = '\0'; + if((pc->server_name = strdup(server_name + matches[1].rm_so)) == NULL) + conf_err("ListenHTTPS: could not set certificate subject"); + } else + conf_err("ListenHTTPS: could not get certificate CN"); +#else + /* no SNI support */ + if(has_other) + conf_err("Cert directives MUST precede other SSL-specific directives - aborted"); + if(res->ctx) + conf_err("ListenHTTPS: multiple certificates not supported - aborted"); + if((res->ctx = malloc(sizeof(POUND_CTX))) == NULL) + conf_err("ListenHTTPS new POUND_CTX: out of memory - aborted"); + res->ctx->server_name = NULL; + res->ctx->next = NULL; + if((res->ctx->ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) + conf_err("SSL_CTX_new failed - aborted"); + if(SSL_CTX_use_certificate_chain_file(res->ctx->ctx, filename) != 1) + conf_err("SSL_CTX_use_certificate_chain_file failed - aborted"); + if(SSL_CTX_use_PrivateKey_file(res->ctx->ctx, filename, SSL_FILETYPE_PEM) != 1) + conf_err("SSL_CTX_use_PrivateKey_file failed - aborted"); + if(SSL_CTX_check_private_key(res->ctx->ctx) != 1) + conf_err("SSL_CTX_check_private_key failed - aborted"); +#endif +} + /* * parse the config file */ @@ -1634,6 +1699,7 @@ config_parse(const int argc, char **const argv) || regcomp(&Address, "^[ \t]*Address[ \t]+([^ \t]+)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&Port, "^[ \t]*Port[ \t]+([1-9][0-9]*)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&Cert, "^[ \t]*Cert[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) + || regcomp(&CertDir, "^[ \t]*CertDir[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&xHTTP, "^[ \t]*xHTTP[ \t]+([01234])[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&Client, "^[ \t]*Client[ \t]+([1-9][0-9]*)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&CheckURL, "^[ \t]*CheckURL[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) @@ -1812,6 +1878,7 @@ config_parse(const int argc, char **const argv) regfree(&Address); regfree(&Port); regfree(&Cert); + regfree(&CertDir); regfree(&xHTTP); regfree(&Client); regfree(&CheckURL); diff --git a/pound.8 b/pound.8 index 0d6e5ce..ec276c0 100644 --- a/pound.8 +++ b/pound.8 @@ -525,12 +525,16 @@ following additional directives are also available: Specify the server certificate. The .I certificate file is the file containing the certificate, possibly a certificate chain and the signature -for this server. This directive is +for this server. This directive or the +.I CertDir +directive is .B mandatory for HTTPS listeners. .IP Please note that multiple .I Cert +or +.I CertDir directives are allowed if your OpenSSL version supports SNI. In such cases, the first directive is the default certificate, with additional certificates used if the client requests them. @@ -542,6 +546,43 @@ most-specific-to-least specific order (i.e. wildcard certificates host-specific certificates). .IP .I Cert +and +.I CertDir +directives +.B must +precede all other SSL-specific directives. +.TP +\fBCertDir\fR "certificate directory" +Specify the server certificate or certificates. The +.I certificate directory +is a directory path containing one or more certificates, possibly a certificate chain and the signature +for this server. This directive or +.I Cert +is +.B mandatory +for HTTPS listeners. +.IP +If a wildcard is specified, it will be honored. Otherwise all files will be loaded from that directory. +For example, "/etc/certs/*.pem" will load all files from that directory that match the file extension given. +.IP +Please note that multiple +.I Cert +or +.I CertDir +directives are allowed if your OpenSSL version supports SNI. In such cases, +the first directive is the default certificate, with additional certificates +used if the client requests them. +.IP +The filenames in the directory will be sorted before being loaded. The order of files +is important: the first certificate where the CN +matches the client request will be used, so sort your files in the +most-specific-to-least specific order (i.e. wildcard certificates +.B after +host-specific certificates). +.IP +.I Cert +and +.I CertDir directives .B must precede all other SSL-specific directives. From 185044a079b3579b22ac8ab72f5f8cf8ee58435f Mon Sep 17 00:00:00 2001 From: Joe Gooch Date: Wed, 3 Jul 2013 11:10:31 -0400 Subject: [PATCH 15/21] OrURLs block, with valid URLs --- config.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- pound.8 | 12 ++++++++++ 2 files changed, 83 insertions(+), 1 deletion(-) diff --git a/config.c b/config.c index 3778b42..0e91366 100644 --- a/config.c +++ b/config.c @@ -76,7 +76,7 @@ static CODE facilitynames[] = { static regex_t Empty, Comment, User, Group, RootJail, Daemon, LogFacility, LogLevel, Alive, SSLEngine, Control; static regex_t ListenHTTP, ListenHTTPS, End, Address, Port, Cert, CertDir, xHTTP, Client, CheckURL; static regex_t Err414, Err500, Err501, Err503, ErrNoSsl, NoSslRedirect, MaxRequest, HeadRemove, RewriteLocation, RewriteDestination; -static regex_t Service, ServiceName, URL, HeadRequire, HeadDeny, BackEnd, Emergency, Priority, HAport, HAportAddr; +static regex_t Service, ServiceName, URL, OrURLs, HeadRequire, HeadDeny, BackEnd, Emergency, Priority, HAport, HAportAddr; static regex_t Redirect, TimeOut, Session, Type, TTL, ID; static regex_t ClientCert, AddHeader, DisableProto, SSLAllowClientRenegotiation, SSLHonorCipherOrder, Ciphers; static regex_t CAlist, VerifyList, CRLlist, NoHTTPS11, Grace, Include, ConnTO, IgnoreCase, HTTPS; @@ -643,6 +643,56 @@ static IMPLEMENT_LHASH_COMP_FN(t, TABNODE) static IMPLEMENT_LHASH_COMP_FN(t_cmp, const TABNODE *) #endif +/* + * parse an OrURLs block + * + * Forms a composite pattern of all URLs within + * of the form ((url1)|(url2)|(url3)) (and so on) + */ +static char * +parse_orurls(void) +{ + char lin[MAXBUF]; + char *pattern; + regex_t comp; + + pattern = NULL; + while(conf_fgets(lin, MAXBUF)) { + if(strlen(lin) > 0 && lin[strlen(lin) - 1] == '\n') + lin[strlen(lin) - 1] = '\0'; + if(!regexec(&URL, lin, 4, matches, 0)) { + lin[matches[1].rm_eo] = '\0'; + /* Verify the pattern is valid */ + if(regcomp(&comp, lin + matches[1].rm_so, REG_NEWLINE | REG_EXTENDED )) + conf_err("URL bad pattern - aborted"); + regfree(&comp); + if (pattern==NULL) { + if((pattern = (char *)malloc(strlen(lin + matches[1].rm_so)+5)) == NULL) + conf_err("OrURLs config: out of memory - aborted"); + *pattern = 0; + strcat(pattern, "(("); + strcat(pattern, lin + matches[1].rm_so); + strcat(pattern, "))"); + } else { + if((pattern = (char *)realloc(pattern, strlen(pattern) + strlen(lin + matches[1].rm_so) + 4)) == NULL) + conf_err("OrURLs config: out of memory - aborted"); + pattern[strlen(pattern)-1]=0; + strcat(pattern, "|("); + strcat(pattern, lin + matches[1].rm_so); + strcat(pattern, "))"); + } + } else if(!regexec(&End, lin, 4, matches, 0)) { + if (!pattern) + conf_err("No URL directives specified within OrURLs block"); + return pattern; + } else { + conf_err("unknown directive"); + } + } + + conf_err("OrURLs premature EOF"); + return NULL; +} /* * parse a service @@ -652,6 +702,7 @@ parse_service(const char *svc_name) { char lin[MAXBUF]; char pat[MAXBUF]; + char *ptr; SERVICE *res; BACKEND *be; MATCHER *m; @@ -690,6 +741,23 @@ parse_service(const char *svc_name) lin[matches[1].rm_eo] = '\0'; if(regcomp(&m->pat, lin + matches[1].rm_so, REG_NEWLINE | REG_EXTENDED | (ign_case? REG_ICASE: 0))) conf_err("URL bad pattern - aborted"); + } else if(!regexec(&OrURLs, lin, 4, matches, 0)) { + if(res->url) { + for(m = res->url; m->next; m = m->next) + ; + if((m->next = (MATCHER *)malloc(sizeof(MATCHER))) == NULL) + conf_err("URL config: out of memory - aborted"); + m = m->next; + } else { + if((res->url = (MATCHER *)malloc(sizeof(MATCHER))) == NULL) + conf_err("URL config: out of memory - aborted"); + m = res->url; + } + memset(m, 0, sizeof(MATCHER)); + ptr = parse_orurls(); + if(regcomp(&m->pat, ptr, REG_NEWLINE | REG_EXTENDED | (ign_case? REG_ICASE: 0))) + conf_err("OrURLs bad pattern - aborted"); + free(ptr); } else if(!regexec(&HeadRequire, lin, 4, matches, 0)) { if(res->req_head) { for(m = res->req_head; m->next; m = m->next) @@ -1716,6 +1784,7 @@ config_parse(const int argc, char **const argv) || regcomp(&Service, "^[ \t]*Service[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&ServiceName, "^[ \t]*Service[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&URL, "^[ \t]*URL[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) + || regcomp(&OrURLs, "^[ \t]*OrURLS[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&BackendCookie, "^[ \t]*BackendCookie[ \t]+\"(.+)\"[ \t]+\"(.*)\"[ \t]+\"(.*)\"[ \t]+([0-9]+|Session)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&HeadRequire, "^[ \t]*HeadRequire[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&HeadDeny, "^[ \t]*HeadDeny[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) @@ -1895,6 +1964,7 @@ config_parse(const int argc, char **const argv) regfree(&Service); regfree(&ServiceName); regfree(&URL); + regfree(&OrURLs); regfree(&BackendCookie); regfree(&HeadRequire); regfree(&HeadDeny); diff --git a/pound.8 b/pound.8 index ec276c0..98ff558 100644 --- a/pound.8 +++ b/pound.8 @@ -687,6 +687,18 @@ was defined then all requests match. The matching is by default case-sensitive, but this can be overridden by specifying .B IgnoreCase 1 .TP +\fBOrURLs\fR +Defines a block of +.I URL +directives that should be merged into a single pattern, all OR'd together. +This creates a pattern like +.B ((url1)|(url2)|(url3)) + for as many +.I URL +directives as are specified within the block. End the block with an +.I End +directive. +.TP \fBBackendCookie\fR "cookiename" "domain" "path" age|Session If defined, Pound will inject a cookie in each response with the appropriate backend's key, so that even if the session table is flushed or sessions are disabled, the proper backend can be chosen. From 347c4762b4bcbe7adf72514736257413577b050f Mon Sep 17 00:00:00 2001 From: kieranjwelch Date: Fri, 15 Nov 2013 09:44:44 +0000 Subject: [PATCH 16/21] Error in usage text for poundctl Fixed a small error in the usage text which is initially confusing, as it suggests you can enable/disable service 'm' in service 'n'. However, the services are in listeners. Therefore, I have modified this text appropriately. --- poundctl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/poundctl.c b/poundctl.c index 45c3e94..3989cf5 100644 --- a/poundctl.c +++ b/poundctl.c @@ -37,8 +37,8 @@ usage(const char *arg0) fprintf(stderr, "\twhere cmd is one of:\n"); fprintf(stderr, "\t-L n - enable listener n\n"); fprintf(stderr, "\t-l n - disable listener n\n"); - fprintf(stderr, "\t-S n m - enable service m in service n (use -1 for global services)\n"); - fprintf(stderr, "\t-s n m - disable service m in service n (use -1 for global services)\n"); + fprintf(stderr, "\t-S n m - enable service m in listener n (use -1 for global services)\n"); + fprintf(stderr, "\t-s n m - disable service m in listener n (use -1 for global services)\n"); fprintf(stderr, "\t-B n m r - enable back-end r in service m in listener n\n"); fprintf(stderr, "\t-b n m r - disable back-end r in service m in listener n\n"); fprintf(stderr, "\t-f n m r - flush all sessions for back-end r in service m in listener n\n"); From 9b7fee7a406e514036cdc62d07244547e0a61f10 Mon Sep 17 00:00:00 2001 From: Brett Parker Date: Sun, 19 Oct 2014 22:25:05 +0100 Subject: [PATCH 17/21] add_mkcalendar_support - Enable MKCALENDAR support as per #742488 --- config.c | 6 +++--- pound.8 | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/config.c b/config.c index 0e91366..0055adc 100644 --- a/config.c +++ b/config.c @@ -98,9 +98,9 @@ static DH *DHCustom_params; static char *xhttp[] = { "^(GET|POST|HEAD) ([^ ]+) HTTP/1.[01]$", "^(GET|POST|HEAD|PUT|PATCH|DELETE) ([^ ]+) HTTP/1.[01]$", - "^(GET|POST|HEAD|PUT|PATCH|DELETE|LOCK|UNLOCK|PROPFIND|PROPPATCH|SEARCH|MKCOL|MOVE|COPY|OPTIONS|TRACE|MKACTIVITY|CHECKOUT|MERGE|REPORT) ([^ ]+) HTTP/1.[01]$", - "^(GET|POST|HEAD|PUT|PATCH|DELETE|LOCK|UNLOCK|PROPFIND|PROPPATCH|SEARCH|MKCOL|MOVE|COPY|OPTIONS|TRACE|MKACTIVITY|CHECKOUT|MERGE|REPORT|SUBSCRIBE|UNSUBSCRIBE|BPROPPATCH|POLL|BMOVE|BCOPY|BDELETE|BPROPFIND|NOTIFY|CONNECT) ([^ ]+) HTTP/1.[01]$", - "^(GET|POST|HEAD|PUT|PATCH|DELETE|LOCK|UNLOCK|PROPFIND|PROPPATCH|SEARCH|MKCOL|MOVE|COPY|OPTIONS|TRACE|MKACTIVITY|CHECKOUT|MERGE|REPORT|SUBSCRIBE|UNSUBSCRIBE|BPROPPATCH|POLL|BMOVE|BCOPY|BDELETE|BPROPFIND|NOTIFY|CONNECT|RPC_IN_DATA|RPC_OUT_DATA) ([^ ]+) HTTP/1.[01]$", + "^(GET|POST|HEAD|PUT|PATCH|DELETE|LOCK|UNLOCK|PROPFIND|PROPPATCH|SEARCH|MKCOL|MKCALENDAR|MOVE|COPY|OPTIONS|TRACE|MKACTIVITY|CHECKOUT|MERGE|REPORT) ([^ ]+) HTTP/1.[01]$", + "^(GET|POST|HEAD|PUT|PATCH|DELETE|LOCK|UNLOCK|PROPFIND|PROPPATCH|SEARCH|MKCOL|MKCALENDAR|MOVE|COPY|OPTIONS|TRACE|MKACTIVITY|CHECKOUT|MERGE|REPORT|SUBSCRIBE|UNSUBSCRIBE|BPROPPATCH|POLL|BMOVE|BCOPY|BDELETE|BPROPFIND|NOTIFY|CONNECT) ([^ ]+) HTTP/1.[01]$", + "^(GET|POST|HEAD|PUT|PATCH|DELETE|LOCK|UNLOCK|PROPFIND|PROPPATCH|SEARCH|MKCOL|MKCALENDAR|MOVE|COPY|OPTIONS|TRACE|MKACTIVITY|CHECKOUT|MERGE|REPORT|SUBSCRIBE|UNSUBSCRIBE|BPROPPATCH|POLL|BMOVE|BCOPY|BDELETE|BPROPFIND|NOTIFY|CONNECT|RPC_IN_DATA|RPC_OUT_DATA) ([^ ]+) HTTP/1.[01]$", }; static int log_level = 1; diff --git a/pound.8 b/pound.8 index 98ff558..15994eb 100644 --- a/pound.8 +++ b/pound.8 @@ -397,7 +397,7 @@ additionally allow extended HTTP requests (PUT, PATCH, DELETE). .I 2 additionally allow standard WebDAV verbs (LOCK, UNLOCK, PROPFIND, PROPPATCH, SEARCH, MKCOL, MOVE, COPY, OPTIONS, TRACE, MKACTIVITY, -CHECKOUT, MERGE, REPORT). +CHECKOUT, MERGE, REPORT, MKCALENDAR). .IP .I 3 additionally allow MS extensions WebDAV verbs (SUBSCRIBE, UNSUBSCRIBE, From 74511e43e7dd104e0be9582e905b2a3aba0522c8 Mon Sep 17 00:00:00 2001 From: Joe Gooch Date: Fri, 10 Jul 2015 16:29:16 -0400 Subject: [PATCH 18/21] include OpenSSL library version in -V --- config.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/config.c b/config.c index 0055adc..608d9ea 100644 --- a/config.c +++ b/config.c @@ -1852,6 +1852,9 @@ config_parse(const int argc, char **const argv) case 'V': print_log = 1; logmsg(LOG_DEBUG, "Version %s", VERSION); +#ifdef SSLEAY_VERSION + logmsg(LOG_DEBUG, "OpenSSL version %s", SSLeay_version(SSLEAY_VERSION)); +#endif logmsg(LOG_DEBUG, " Configuration switches:"); #ifdef C_SUPER if(strcmp(C_SUPER, "0")) From 631708050730b64db37eb4ff072b60f31235bd71 Mon Sep 17 00:00:00 2001 From: nyov Date: Tue, 3 Jul 2018 09:04:53 +0000 Subject: [PATCH 19/21] remove useless dependency on libresolv.so (was introduced with dynscale in v2.2 ?) --- configure | 40 ---------------------------------------- 1 file changed, 40 deletions(-) diff --git a/configure b/configure index e44ecb8..16f6664 100755 --- a/configure +++ b/configure @@ -3937,46 +3937,6 @@ if test "x$ac_cv_lib_socket_socket" = xyes; then : LIBS="-lsocket -lnsl ${LIBS}" fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for hstrerror in -lresolv" >&5 -$as_echo_n "checking for hstrerror in -lresolv... " >&6; } -if ${ac_cv_lib_resolv_hstrerror+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lresolv $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char hstrerror (); -int -main () -{ -return hstrerror (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - ac_cv_lib_resolv_hstrerror=yes -else - ac_cv_lib_resolv_hstrerror=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_resolv_hstrerror" >&5 -$as_echo "$ac_cv_lib_resolv_hstrerror" >&6; } -if test "x$ac_cv_lib_resolv_hstrerror" = xyes; then : - LIBS="-lresolv ${LIBS}" -fi - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for BIO_new in -lcrypto" >&5 $as_echo_n "checking for BIO_new in -lcrypto... " >&6; } if ${ac_cv_lib_crypto_BIO_new+:} false; then : From 27925a3353d645af156f47bc5652781b9303168e Mon Sep 17 00:00:00 2001 From: nyov Date: Tue, 3 Jul 2018 09:08:24 +0000 Subject: [PATCH 20/21] remove useless dependency on libm.so (was introduced with dynscale in v2.2 ?) --- configure | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/configure b/configure index 16f6664..19a5e0e 100755 --- a/configure +++ b/configure @@ -3851,7 +3851,8 @@ fi { $as_echo "$as_me:${as_lineno-$LINENO}: *** Checking for libraries ***" >&5 $as_echo "$as_me: *** Checking for libraries ***" >&6;} -LIBS="${LIBS} -lm" + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 $as_echo_n "checking for dlopen in -ldl... " >&6; } if ${ac_cv_lib_dl_dlopen+:} false; then : From dda4f9228239844afdf3bc2e171dac17632bb955 Mon Sep 17 00:00:00 2001 From: nyov Date: Tue, 3 Jul 2018 09:11:07 +0000 Subject: [PATCH 21/21] remove apparently useless dependency on libdl.so --- configure | 45 --------------------------------------------- 1 file changed, 45 deletions(-) diff --git a/configure b/configure index 19a5e0e..0ba56e5 100755 --- a/configure +++ b/configure @@ -3853,51 +3853,6 @@ fi $as_echo "$as_me: *** Checking for libraries ***" >&6;} -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 -$as_echo_n "checking for dlopen in -ldl... " >&6; } -if ${ac_cv_lib_dl_dlopen+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-ldl $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char dlopen (); -int -main () -{ -return dlopen (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - ac_cv_lib_dl_dlopen=yes -else - ac_cv_lib_dl_dlopen=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 -$as_echo "$ac_cv_lib_dl_dlopen" >&6; } -if test "x$ac_cv_lib_dl_dlopen" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_LIBDL 1 -_ACEOF - - LIBS="-ldl $LIBS" - -fi - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for socket in -lsocket" >&5 $as_echo_n "checking for socket in -lsocket... " >&6; } if ${ac_cv_lib_socket_socket+:} false; then :