]> granicus.if.org Git - apache/commitdiff
Initial revision
authorRalf S. Engelschall <rse@apache.org>
Fri, 4 May 2001 21:54:25 +0000 (21:54 +0000)
committerRalf S. Engelschall <rse@apache.org>
Fri, 4 May 2001 21:54:25 +0000 (21:54 +0000)
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@88988 13f79535-47bb-0310-9956-ffa450edef68

42 files changed:
modules/ssl/Makefile.libdir [new file with mode: 0644]
modules/ssl/Makefile.tmpl [new file with mode: 0644]
modules/ssl/Makefile.win32 [new file with mode: 0644]
modules/ssl/README [new file with mode: 0644]
modules/ssl/README.dsov.fig [new file with mode: 0644]
modules/ssl/README.dsov.ps [new file with mode: 0644]
modules/ssl/libssl.module [new file with mode: 0644]
modules/ssl/libssl.version [new file with mode: 0644]
modules/ssl/mod_ssl.c [new file with mode: 0644]
modules/ssl/mod_ssl.h [new file with mode: 0644]
modules/ssl/ssl_engine_compat.c [new file with mode: 0644]
modules/ssl/ssl_engine_config.c [new file with mode: 0644]
modules/ssl/ssl_engine_dh.c [new file with mode: 0644]
modules/ssl/ssl_engine_ds.c [new file with mode: 0644]
modules/ssl/ssl_engine_ext.c [new file with mode: 0644]
modules/ssl/ssl_engine_init.c [new file with mode: 0644]
modules/ssl/ssl_engine_io.c [new file with mode: 0644]
modules/ssl/ssl_engine_kernel.c [new file with mode: 0644]
modules/ssl/ssl_engine_log.c [new file with mode: 0644]
modules/ssl/ssl_engine_mutex.c [new file with mode: 0644]
modules/ssl/ssl_engine_pphrase.c [new file with mode: 0644]
modules/ssl/ssl_engine_rand.c [new file with mode: 0644]
modules/ssl/ssl_engine_vars.c [new file with mode: 0644]
modules/ssl/ssl_expr.c [new file with mode: 0644]
modules/ssl/ssl_expr.h [new file with mode: 0644]
modules/ssl/ssl_expr_eval.c [new file with mode: 0644]
modules/ssl/ssl_expr_parse.c [new file with mode: 0644]
modules/ssl/ssl_expr_parse.h [new file with mode: 0644]
modules/ssl/ssl_expr_parse.y [new file with mode: 0644]
modules/ssl/ssl_expr_scan.c [new file with mode: 0644]
modules/ssl/ssl_expr_scan.l [new file with mode: 0644]
modules/ssl/ssl_scache.c [new file with mode: 0644]
modules/ssl/ssl_scache_dbm.c [new file with mode: 0644]
modules/ssl/ssl_scache_shmcb.c [new file with mode: 0644]
modules/ssl/ssl_scache_shmht.c [new file with mode: 0644]
modules/ssl/ssl_util.c [new file with mode: 0644]
modules/ssl/ssl_util_sdbm.c [new file with mode: 0644]
modules/ssl/ssl_util_sdbm.h [new file with mode: 0644]
modules/ssl/ssl_util_ssl.c [new file with mode: 0644]
modules/ssl/ssl_util_ssl.h [new file with mode: 0644]
modules/ssl/ssl_util_table.c [new file with mode: 0644]
modules/ssl/ssl_util_table.h [new file with mode: 0644]

diff --git a/modules/ssl/Makefile.libdir b/modules/ssl/Makefile.libdir
new file mode 100644 (file)
index 0000000..a4a4c32
--- /dev/null
@@ -0,0 +1,15 @@
+##                      _             _ 
+##  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
+## | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
+## | | | | | | (_) | (_| |   \__ \__ \ |  www.modssl.org
+## |_| |_| |_|\___/ \__,_|___|___/___/_|  ftp.modssl.org
+##                      |_____|         
+##  Makefile.libdir 
+##  Apache 1.3 Configuration mechanism indicator file
+##
+
+This is a place-holder which indicates to Apache's Configure script that it
+shouldn't provide the default targets when building the Makefile in this
+directory.  Instead it'll just prepend all the important variable definitions,
+and copy the Makefile.tmpl onto the end.
+
diff --git a/modules/ssl/Makefile.tmpl b/modules/ssl/Makefile.tmpl
new file mode 100644 (file)
index 0000000..3816273
--- /dev/null
@@ -0,0 +1,529 @@
+##                      _             _ 
+##  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
+## | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
+## | | | | | | (_) | (_| |   \__ \__ \ |  www.modssl.org
+## |_| |_| |_|\___/ \__,_|___|___/___/_|  ftp.modssl.org
+##                      |_____|         
+##  Makefile.tmpl 
+##  Apache 1.3 Makefile template for SSL module (Unix environment)
+##
+
+##  ====================================================================
+##  Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+## 
+##  Redistribution and use in source and binary forms, with or without
+##  modification, are permitted provided that the following conditions
+##  are met:
+## 
+##  1. Redistributions of source code must retain the above copyright
+##     notice, this list of conditions and the following disclaimer. 
+## 
+##  2. Redistributions in binary form must reproduce the above copyright
+##     notice, this list of conditions and the following
+##     disclaimer in the documentation and/or other materials
+##     provided with the distribution.
+## 
+##  3. All advertising materials mentioning features or use of this
+##     software must display the following acknowledgment:
+##     "This product includes software developed by 
+##      Ralf S. Engelschall <rse@engelschall.com> for use in the
+##      mod_ssl project (http://www.modssl.org/)."
+## 
+##  4. The names "mod_ssl" must not be used to endorse or promote
+##     products derived from this software without prior written
+##     permission. For written permission, please contact
+##     rse@engelschall.com.
+## 
+##  5. Products derived from this software may not be called "mod_ssl"
+##     nor may "mod_ssl" appear in their names without prior
+##     written permission of Ralf S. Engelschall.
+## 
+##  6. Redistributions of any form whatsoever must retain the following
+##     acknowledgment:
+##     "This product includes software developed by 
+##      Ralf S. Engelschall <rse@engelschall.com> for use in the
+##      mod_ssl project (http://www.modssl.org/)."
+## 
+##  THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+##  EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+##  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+##  PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+##  HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+##  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+##  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+##  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+##  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+##  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+##  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+##  OF THE POSSIBILITY OF SUCH DAMAGE.
+##  ====================================================================
+##
+                             #
+                             # ``I cannot write a program which is
+                             #   as popular as one from Larry Wall.
+                             #   But I can write one which is from me.''
+                             #                     -- RSE
+
+LIB=libssl.$(LIBEXT)
+
+OBJS=\
+ mod_ssl.o\
+ ssl_engine_config.o\
+ ssl_engine_compat.o\
+ ssl_engine_ds.o\
+ ssl_engine_dh.o\
+ ssl_engine_init.o\
+ ssl_engine_kernel.o\
+ ssl_engine_rand.o\
+ ssl_engine_io.o\
+ ssl_engine_log.o\
+ ssl_engine_mutex.o\
+ ssl_engine_pphrase.o\
+ ssl_engine_vars.o\
+ ssl_engine_ext.o\
+ ssl_scache.o\
+ ssl_scache_dbm.o\
+ ssl_scache_shmht.o\
+ ssl_scache_shmcb.o\
+ ssl_expr.o\
+ ssl_expr_scan.o\
+ ssl_expr_parse.o\
+ ssl_expr_eval.o\
+ ssl_util.o\
+ ssl_util_ssl.o\
+ ssl_util_sdbm.o\
+ ssl_util_table.o\
+ $(SSL_VENDOR_OBJS)
+
+OBJS_PIC=\
+ mod_ssl.lo\
+ ssl_engine_config.lo\
+ ssl_engine_compat.lo\
+ ssl_engine_ds.lo\
+ ssl_engine_dh.lo\
+ ssl_engine_init.lo\
+ ssl_engine_kernel.lo\
+ ssl_engine_rand.lo\
+ ssl_engine_io.lo\
+ ssl_engine_log.lo\
+ ssl_engine_mutex.lo\
+ ssl_engine_pphrase.lo\
+ ssl_engine_vars.lo\
+ ssl_engine_ext.lo\
+ ssl_scache.lo\
+ ssl_scache_dbm.lo\
+ ssl_scache_shmht.lo\
+ ssl_scache_shmcb.lo\
+ ssl_expr.lo\
+ ssl_expr_scan.lo\
+ ssl_expr_parse.lo\
+ ssl_expr_eval.lo\
+ ssl_util.lo\
+ ssl_util_ssl.lo\
+ ssl_util_sdbm.lo\
+ ssl_util_table.lo\
+ $(SSL_VENDOR_OBJS_PIC)
+
+##
+##  END-USER AREA
+##
+
+all: lib
+
+lib: $(LIB)
+
+libssl.a: $(OBJS)
+       rm -f $@
+       ar cr $@ $(OBJS)
+       $(RANLIB) $@
+
+libssl.so: $(OBJS_PIC)
+       rm -f $@
+       $(LD_SHLIB) $(SSL_LDFLAGS) $(LDFLAGS_SHLIB) -o $@ $(OBJS_PIC) $(SSL_LIBS) $(LIBS_SHLIB) 
+
+.SUFFIXES: .o .lo
+
+.c.o:
+       $(CC) -c $(INCLUDES) $(CFLAGS) $(SSL_CFLAGS) $(SSL_VERSION) $<
+
+.c.lo:
+       $(CC) -c $(INCLUDES) $(CFLAGS) $(CFLAGS_SHLIB) $(SSL_CFLAGS) $(SSL_VERSION) $< && mv $*.o $*.lo
+
+clean:
+       rm -f $(OBJS) $(OBJS_PIC) 
+       rm -f libssl.a libssl.so
+
+realclean: clean
+       rm -f ssl_expr_parse.c ssl_expr_parse.h
+       rm -f ssl_expr_scan.c
+
+distclean: clean
+       -rm -f Makefile
+
+##
+##  DEVELOPER AREA
+##  We really don't expect end users to use these targets!
+##
+
+ssl_expr_scan.c: ssl_expr_scan.l ssl_expr_parse.h
+       flex -Pssl_expr_yy -s -B ssl_expr_scan.l
+       sed -e '/$$Header:/d' <lex.ssl_expr_yy.c >ssl_expr_scan.c && rm -f lex.ssl_expr_yy.c
+
+ssl_expr_parse.c ssl_expr_parse.h: ssl_expr_parse.y
+       yacc -d ssl_expr_parse.y
+       sed -e 's;yy;ssl_expr_yy;g' \
+           -e '/#if defined(c_plusplus) || defined(__cplusplus)/,/#endif/d' \
+           <y.tab.c >ssl_expr_parse.c && rm -f y.tab.c
+       sed -e 's;yy;ssl_expr_yy;g' \
+           <y.tab.h >ssl_expr_parse.h && rm -f y.tab.h
+
+nocons:
+       @$(MAKE) $(MFLAGS) $(MFLAGS_STATIC) \
+            SSL_CFLAGS="`echo $(SSL_CFLAGS) |\
+            sed -e 's;-DSSL_CONSERVATIVE;;'`" all
+
+cons:
+       @$(MAKE) $(MFLAGS) $(MFLAGS_STATIC) \
+            SSL_CFLAGS="`echo $(SSL_CFLAGS) |\
+            sed -e 's;-DSSL_CONSERVATIVE;;' \
+                -e 's;^;-DSSL_CONSERVATIVE ;'`" all
+noexp:
+       @$(MAKE) $(MFLAGS) $(MFLAGS_STATIC) \
+            SSL_CFLAGS="`echo $(SSL_CFLAGS) |\
+            sed -e 's;-DSSL_EXPERIMENTAL;;'`" all
+
+exp:
+       @$(MAKE) $(MFLAGS) $(MFLAGS_STATIC) \
+            SSL_CFLAGS="`echo $(SSL_CFLAGS) |\
+            sed -e 's;-DSSL_EXPERIMENTAL;;' \
+                -e 's;^;-DSSL_EXPERIMENTAL ;'`" all
+
+depend:
+       cp Makefile.tmpl Makefile.tmpl.bak \
+           && sed -ne '1,/^# DO NOT REMOVE/p' Makefile.tmpl > Makefile.new \
+           && gcc -MM $(INCLUDES) $(CFLAGS) $(SSL_CFLAGS) *.c >> Makefile.new \
+           && sed -e '1,$$s; $(INCDIR)/; $$(INCDIR)/;g' \
+                  -e '1,$$s; $(OSDIR)/; $$(OSDIR)/;g' \
+                  -e '1,$$s;^\([a-z0-9_]*\)\.o:;\1.o \1.lo:;g' Makefile.new \
+               > Makefile.tmpl \
+           && rm Makefile.new
+
+##
+##  DEPENDENCY AREA
+##
+
+$(OBJS) $(OBJS_PIC): Makefile
+
+# DO NOT REMOVE
+mod_ssl.o mod_ssl.lo: mod_ssl.c mod_ssl.h $(INCDIR)/ap_config.h \
+ $(INCDIR)/ap_mmn.h $(INCDIR)/ap_config_auto.h \
+ $(OSDIR)/os.h $(OSDIR)/os-inline.c $(INCDIR)/ap_ctype.h \
+ $(INCDIR)/httpd.h $(INCDIR)/ap_mm.h $(INCDIR)/ap_alloc.h \
+ $(INCDIR)/ap_hook.h $(INCDIR)/ap_ctx.h $(INCDIR)/buff.h \
+ $(INCDIR)/ap.h $(INCDIR)/util_uri.h \
+ $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \
+ $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \
+ $(INCDIR)/http_main.h $(INCDIR)/http_core.h \
+ $(INCDIR)/http_log.h $(INCDIR)/scoreboard.h \
+ $(INCDIR)/util_md5.h $(INCDIR)/ap_md5.h \
+ $(INCDIR)/fnmatch.h ssl_expr.h ssl_util_ssl.h ssl_util_table.h
+ssl_engine_compat.o ssl_engine_compat.lo: ssl_engine_compat.c mod_ssl.h \
+ $(INCDIR)/ap_config.h $(INCDIR)/ap_mmn.h \
+ $(INCDIR)/ap_config_auto.h $(OSDIR)/os.h \
+ $(OSDIR)/os-inline.c $(INCDIR)/ap_ctype.h \
+ $(INCDIR)/httpd.h $(INCDIR)/ap_mm.h $(INCDIR)/ap_alloc.h \
+ $(INCDIR)/ap_hook.h $(INCDIR)/ap_ctx.h $(INCDIR)/buff.h \
+ $(INCDIR)/ap.h $(INCDIR)/util_uri.h \
+ $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \
+ $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \
+ $(INCDIR)/http_main.h $(INCDIR)/http_core.h \
+ $(INCDIR)/http_log.h $(INCDIR)/scoreboard.h \
+ $(INCDIR)/util_md5.h $(INCDIR)/ap_md5.h \
+ $(INCDIR)/fnmatch.h ssl_expr.h ssl_util_ssl.h ssl_util_table.h
+ssl_engine_config.o ssl_engine_config.lo: ssl_engine_config.c mod_ssl.h \
+ $(INCDIR)/ap_config.h $(INCDIR)/ap_mmn.h \
+ $(INCDIR)/ap_config_auto.h $(OSDIR)/os.h \
+ $(OSDIR)/os-inline.c $(INCDIR)/ap_ctype.h \
+ $(INCDIR)/httpd.h $(INCDIR)/ap_mm.h $(INCDIR)/ap_alloc.h \
+ $(INCDIR)/ap_hook.h $(INCDIR)/ap_ctx.h $(INCDIR)/buff.h \
+ $(INCDIR)/ap.h $(INCDIR)/util_uri.h \
+ $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \
+ $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \
+ $(INCDIR)/http_main.h $(INCDIR)/http_core.h \
+ $(INCDIR)/http_log.h $(INCDIR)/scoreboard.h \
+ $(INCDIR)/util_md5.h $(INCDIR)/ap_md5.h \
+ $(INCDIR)/fnmatch.h ssl_expr.h ssl_util_ssl.h ssl_util_table.h
+ssl_engine_dh.o ssl_engine_dh.lo: ssl_engine_dh.c mod_ssl.h $(INCDIR)/ap_config.h \
+ $(INCDIR)/ap_mmn.h $(INCDIR)/ap_config_auto.h \
+ $(OSDIR)/os.h $(OSDIR)/os-inline.c $(INCDIR)/ap_ctype.h \
+ $(INCDIR)/httpd.h $(INCDIR)/ap_mm.h $(INCDIR)/ap_alloc.h \
+ $(INCDIR)/ap_hook.h $(INCDIR)/ap_ctx.h $(INCDIR)/buff.h \
+ $(INCDIR)/ap.h $(INCDIR)/util_uri.h \
+ $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \
+ $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \
+ $(INCDIR)/http_main.h $(INCDIR)/http_core.h \
+ $(INCDIR)/http_log.h $(INCDIR)/scoreboard.h \
+ $(INCDIR)/util_md5.h $(INCDIR)/ap_md5.h \
+ $(INCDIR)/fnmatch.h ssl_expr.h ssl_util_ssl.h ssl_util_table.h
+ssl_engine_ds.o ssl_engine_ds.lo: ssl_engine_ds.c mod_ssl.h $(INCDIR)/ap_config.h \
+ $(INCDIR)/ap_mmn.h $(INCDIR)/ap_config_auto.h \
+ $(OSDIR)/os.h $(OSDIR)/os-inline.c $(INCDIR)/ap_ctype.h \
+ $(INCDIR)/httpd.h $(INCDIR)/ap_mm.h $(INCDIR)/ap_alloc.h \
+ $(INCDIR)/ap_hook.h $(INCDIR)/ap_ctx.h $(INCDIR)/buff.h \
+ $(INCDIR)/ap.h $(INCDIR)/util_uri.h \
+ $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \
+ $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \
+ $(INCDIR)/http_main.h $(INCDIR)/http_core.h \
+ $(INCDIR)/http_log.h $(INCDIR)/scoreboard.h \
+ $(INCDIR)/util_md5.h $(INCDIR)/ap_md5.h \
+ $(INCDIR)/fnmatch.h ssl_expr.h ssl_util_ssl.h ssl_util_table.h
+ssl_engine_ext.o ssl_engine_ext.lo: ssl_engine_ext.c mod_ssl.h $(INCDIR)/ap_config.h \
+ $(INCDIR)/ap_mmn.h $(INCDIR)/ap_config_auto.h \
+ $(OSDIR)/os.h $(OSDIR)/os-inline.c $(INCDIR)/ap_ctype.h \
+ $(INCDIR)/httpd.h $(INCDIR)/ap_mm.h $(INCDIR)/ap_alloc.h \
+ $(INCDIR)/ap_hook.h $(INCDIR)/ap_ctx.h $(INCDIR)/buff.h \
+ $(INCDIR)/ap.h $(INCDIR)/util_uri.h \
+ $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \
+ $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \
+ $(INCDIR)/http_main.h $(INCDIR)/http_core.h \
+ $(INCDIR)/http_log.h $(INCDIR)/scoreboard.h \
+ $(INCDIR)/util_md5.h $(INCDIR)/ap_md5.h \
+ $(INCDIR)/fnmatch.h ssl_expr.h ssl_util_ssl.h ssl_util_table.h
+ssl_engine_init.o ssl_engine_init.lo: ssl_engine_init.c mod_ssl.h \
+ $(INCDIR)/ap_config.h $(INCDIR)/ap_mmn.h \
+ $(INCDIR)/ap_config_auto.h $(OSDIR)/os.h \
+ $(OSDIR)/os-inline.c $(INCDIR)/ap_ctype.h \
+ $(INCDIR)/httpd.h $(INCDIR)/ap_mm.h $(INCDIR)/ap_alloc.h \
+ $(INCDIR)/ap_hook.h $(INCDIR)/ap_ctx.h $(INCDIR)/buff.h \
+ $(INCDIR)/ap.h $(INCDIR)/util_uri.h \
+ $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \
+ $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \
+ $(INCDIR)/http_main.h $(INCDIR)/http_core.h \
+ $(INCDIR)/http_log.h $(INCDIR)/scoreboard.h \
+ $(INCDIR)/util_md5.h $(INCDIR)/ap_md5.h \
+ $(INCDIR)/fnmatch.h ssl_expr.h ssl_util_ssl.h ssl_util_table.h
+ssl_engine_io.o ssl_engine_io.lo: ssl_engine_io.c mod_ssl.h $(INCDIR)/ap_config.h \
+ $(INCDIR)/ap_mmn.h $(INCDIR)/ap_config_auto.h \
+ $(OSDIR)/os.h $(OSDIR)/os-inline.c $(INCDIR)/ap_ctype.h \
+ $(INCDIR)/httpd.h $(INCDIR)/ap_mm.h $(INCDIR)/ap_alloc.h \
+ $(INCDIR)/ap_hook.h $(INCDIR)/ap_ctx.h $(INCDIR)/buff.h \
+ $(INCDIR)/ap.h $(INCDIR)/util_uri.h \
+ $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \
+ $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \
+ $(INCDIR)/http_main.h $(INCDIR)/http_core.h \
+ $(INCDIR)/http_log.h $(INCDIR)/scoreboard.h \
+ $(INCDIR)/util_md5.h $(INCDIR)/ap_md5.h \
+ $(INCDIR)/fnmatch.h ssl_expr.h ssl_util_ssl.h ssl_util_table.h
+ssl_engine_kernel.o ssl_engine_kernel.lo: ssl_engine_kernel.c mod_ssl.h \
+ $(INCDIR)/ap_config.h $(INCDIR)/ap_mmn.h \
+ $(INCDIR)/ap_config_auto.h $(OSDIR)/os.h \
+ $(OSDIR)/os-inline.c $(INCDIR)/ap_ctype.h \
+ $(INCDIR)/httpd.h $(INCDIR)/ap_mm.h $(INCDIR)/ap_alloc.h \
+ $(INCDIR)/ap_hook.h $(INCDIR)/ap_ctx.h $(INCDIR)/buff.h \
+ $(INCDIR)/ap.h $(INCDIR)/util_uri.h \
+ $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \
+ $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \
+ $(INCDIR)/http_main.h $(INCDIR)/http_core.h \
+ $(INCDIR)/http_log.h $(INCDIR)/scoreboard.h \
+ $(INCDIR)/util_md5.h $(INCDIR)/ap_md5.h \
+ $(INCDIR)/fnmatch.h ssl_expr.h ssl_util_ssl.h ssl_util_table.h
+ssl_engine_log.o ssl_engine_log.lo: ssl_engine_log.c mod_ssl.h $(INCDIR)/ap_config.h \
+ $(INCDIR)/ap_mmn.h $(INCDIR)/ap_config_auto.h \
+ $(OSDIR)/os.h $(OSDIR)/os-inline.c $(INCDIR)/ap_ctype.h \
+ $(INCDIR)/httpd.h $(INCDIR)/ap_mm.h $(INCDIR)/ap_alloc.h \
+ $(INCDIR)/ap_hook.h $(INCDIR)/ap_ctx.h $(INCDIR)/buff.h \
+ $(INCDIR)/ap.h $(INCDIR)/util_uri.h \
+ $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \
+ $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \
+ $(INCDIR)/http_main.h $(INCDIR)/http_core.h \
+ $(INCDIR)/http_log.h $(INCDIR)/scoreboard.h \
+ $(INCDIR)/util_md5.h $(INCDIR)/ap_md5.h \
+ $(INCDIR)/fnmatch.h ssl_expr.h ssl_util_ssl.h ssl_util_table.h
+ssl_engine_mutex.o ssl_engine_mutex.lo: ssl_engine_mutex.c mod_ssl.h \
+ $(INCDIR)/ap_config.h $(INCDIR)/ap_mmn.h \
+ $(INCDIR)/ap_config_auto.h $(OSDIR)/os.h \
+ $(OSDIR)/os-inline.c $(INCDIR)/ap_ctype.h \
+ $(INCDIR)/httpd.h $(INCDIR)/ap_mm.h $(INCDIR)/ap_alloc.h \
+ $(INCDIR)/ap_hook.h $(INCDIR)/ap_ctx.h $(INCDIR)/buff.h \
+ $(INCDIR)/ap.h $(INCDIR)/util_uri.h \
+ $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \
+ $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \
+ $(INCDIR)/http_main.h $(INCDIR)/http_core.h \
+ $(INCDIR)/http_log.h $(INCDIR)/scoreboard.h \
+ $(INCDIR)/util_md5.h $(INCDIR)/ap_md5.h \
+ $(INCDIR)/fnmatch.h ssl_expr.h ssl_util_ssl.h ssl_util_table.h
+ssl_engine_pphrase.o ssl_engine_pphrase.lo: ssl_engine_pphrase.c mod_ssl.h \
+ $(INCDIR)/ap_config.h $(INCDIR)/ap_mmn.h \
+ $(INCDIR)/ap_config_auto.h $(OSDIR)/os.h \
+ $(OSDIR)/os-inline.c $(INCDIR)/ap_ctype.h \
+ $(INCDIR)/httpd.h $(INCDIR)/ap_mm.h $(INCDIR)/ap_alloc.h \
+ $(INCDIR)/ap_hook.h $(INCDIR)/ap_ctx.h $(INCDIR)/buff.h \
+ $(INCDIR)/ap.h $(INCDIR)/util_uri.h \
+ $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \
+ $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \
+ $(INCDIR)/http_main.h $(INCDIR)/http_core.h \
+ $(INCDIR)/http_log.h $(INCDIR)/scoreboard.h \
+ $(INCDIR)/util_md5.h $(INCDIR)/ap_md5.h \
+ $(INCDIR)/fnmatch.h ssl_expr.h ssl_util_ssl.h ssl_util_table.h
+ssl_engine_rand.o ssl_engine_rand.lo: ssl_engine_rand.c mod_ssl.h \
+ $(INCDIR)/ap_config.h $(INCDIR)/ap_mmn.h \
+ $(INCDIR)/ap_config_auto.h $(OSDIR)/os.h \
+ $(OSDIR)/os-inline.c $(INCDIR)/ap_ctype.h \
+ $(INCDIR)/httpd.h $(INCDIR)/ap_mm.h $(INCDIR)/ap_alloc.h \
+ $(INCDIR)/ap_hook.h $(INCDIR)/ap_ctx.h $(INCDIR)/buff.h \
+ $(INCDIR)/ap.h $(INCDIR)/util_uri.h \
+ $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \
+ $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \
+ $(INCDIR)/http_main.h $(INCDIR)/http_core.h \
+ $(INCDIR)/http_log.h $(INCDIR)/scoreboard.h \
+ $(INCDIR)/util_md5.h $(INCDIR)/ap_md5.h \
+ $(INCDIR)/fnmatch.h ssl_expr.h ssl_util_ssl.h ssl_util_table.h
+ssl_engine_vars.o ssl_engine_vars.lo: ssl_engine_vars.c mod_ssl.h \
+ $(INCDIR)/ap_config.h $(INCDIR)/ap_mmn.h \
+ $(INCDIR)/ap_config_auto.h $(OSDIR)/os.h \
+ $(OSDIR)/os-inline.c $(INCDIR)/ap_ctype.h \
+ $(INCDIR)/httpd.h $(INCDIR)/ap_mm.h $(INCDIR)/ap_alloc.h \
+ $(INCDIR)/ap_hook.h $(INCDIR)/ap_ctx.h $(INCDIR)/buff.h \
+ $(INCDIR)/ap.h $(INCDIR)/util_uri.h \
+ $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \
+ $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \
+ $(INCDIR)/http_main.h $(INCDIR)/http_core.h \
+ $(INCDIR)/http_log.h $(INCDIR)/scoreboard.h \
+ $(INCDIR)/util_md5.h $(INCDIR)/ap_md5.h \
+ $(INCDIR)/fnmatch.h ssl_expr.h ssl_util_ssl.h ssl_util_table.h
+ssl_expr.o ssl_expr.lo: ssl_expr.c mod_ssl.h $(INCDIR)/ap_config.h \
+ $(INCDIR)/ap_mmn.h $(INCDIR)/ap_config_auto.h \
+ $(OSDIR)/os.h $(OSDIR)/os-inline.c $(INCDIR)/ap_ctype.h \
+ $(INCDIR)/httpd.h $(INCDIR)/ap_mm.h $(INCDIR)/ap_alloc.h \
+ $(INCDIR)/ap_hook.h $(INCDIR)/ap_ctx.h $(INCDIR)/buff.h \
+ $(INCDIR)/ap.h $(INCDIR)/util_uri.h \
+ $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \
+ $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \
+ $(INCDIR)/http_main.h $(INCDIR)/http_core.h \
+ $(INCDIR)/http_log.h $(INCDIR)/scoreboard.h \
+ $(INCDIR)/util_md5.h $(INCDIR)/ap_md5.h \
+ $(INCDIR)/fnmatch.h ssl_expr.h ssl_util_ssl.h ssl_util_table.h
+ssl_expr_eval.o ssl_expr_eval.lo: ssl_expr_eval.c mod_ssl.h $(INCDIR)/ap_config.h \
+ $(INCDIR)/ap_mmn.h $(INCDIR)/ap_config_auto.h \
+ $(OSDIR)/os.h $(OSDIR)/os-inline.c $(INCDIR)/ap_ctype.h \
+ $(INCDIR)/httpd.h $(INCDIR)/ap_mm.h $(INCDIR)/ap_alloc.h \
+ $(INCDIR)/ap_hook.h $(INCDIR)/ap_ctx.h $(INCDIR)/buff.h \
+ $(INCDIR)/ap.h $(INCDIR)/util_uri.h \
+ $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \
+ $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \
+ $(INCDIR)/http_main.h $(INCDIR)/http_core.h \
+ $(INCDIR)/http_log.h $(INCDIR)/scoreboard.h \
+ $(INCDIR)/util_md5.h $(INCDIR)/ap_md5.h \
+ $(INCDIR)/fnmatch.h ssl_expr.h ssl_util_ssl.h ssl_util_table.h
+ssl_expr_parse.o ssl_expr_parse.lo: ssl_expr_parse.c mod_ssl.h $(INCDIR)/ap_config.h \
+ $(INCDIR)/ap_mmn.h $(INCDIR)/ap_config_auto.h \
+ $(OSDIR)/os.h $(OSDIR)/os-inline.c $(INCDIR)/ap_ctype.h \
+ $(INCDIR)/httpd.h $(INCDIR)/ap_mm.h $(INCDIR)/ap_alloc.h \
+ $(INCDIR)/ap_hook.h $(INCDIR)/ap_ctx.h $(INCDIR)/buff.h \
+ $(INCDIR)/ap.h $(INCDIR)/util_uri.h \
+ $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \
+ $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \
+ $(INCDIR)/http_main.h $(INCDIR)/http_core.h \
+ $(INCDIR)/http_log.h $(INCDIR)/scoreboard.h \
+ $(INCDIR)/util_md5.h $(INCDIR)/ap_md5.h \
+ $(INCDIR)/fnmatch.h ssl_expr.h ssl_util_ssl.h ssl_util_table.h
+ssl_expr_scan.o ssl_expr_scan.lo: ssl_expr_scan.c mod_ssl.h $(INCDIR)/ap_config.h \
+ $(INCDIR)/ap_mmn.h $(INCDIR)/ap_config_auto.h \
+ $(OSDIR)/os.h $(OSDIR)/os-inline.c $(INCDIR)/ap_ctype.h \
+ $(INCDIR)/httpd.h $(INCDIR)/ap_mm.h $(INCDIR)/ap_alloc.h \
+ $(INCDIR)/ap_hook.h $(INCDIR)/ap_ctx.h $(INCDIR)/buff.h \
+ $(INCDIR)/ap.h $(INCDIR)/util_uri.h \
+ $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \
+ $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \
+ $(INCDIR)/http_main.h $(INCDIR)/http_core.h \
+ $(INCDIR)/http_log.h $(INCDIR)/scoreboard.h \
+ $(INCDIR)/util_md5.h $(INCDIR)/ap_md5.h \
+ $(INCDIR)/fnmatch.h ssl_expr.h ssl_util_ssl.h ssl_util_table.h \
+ ssl_expr_parse.h
+ssl_scache.o ssl_scache.lo: ssl_scache.c mod_ssl.h $(INCDIR)/ap_config.h \
+ $(INCDIR)/ap_mmn.h $(INCDIR)/ap_config_auto.h \
+ $(OSDIR)/os.h $(OSDIR)/os-inline.c $(INCDIR)/ap_ctype.h \
+ $(INCDIR)/httpd.h $(INCDIR)/ap_mm.h $(INCDIR)/ap_alloc.h \
+ $(INCDIR)/ap_hook.h $(INCDIR)/ap_ctx.h $(INCDIR)/buff.h \
+ $(INCDIR)/ap.h $(INCDIR)/util_uri.h \
+ $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \
+ $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \
+ $(INCDIR)/http_main.h $(INCDIR)/http_core.h \
+ $(INCDIR)/http_log.h $(INCDIR)/scoreboard.h \
+ $(INCDIR)/util_md5.h $(INCDIR)/ap_md5.h \
+ $(INCDIR)/fnmatch.h ssl_expr.h ssl_util_ssl.h ssl_util_table.h
+ssl_scache_dbm.o ssl_scache_dbm.lo: ssl_scache_dbm.c mod_ssl.h $(INCDIR)/ap_config.h \
+ $(INCDIR)/ap_mmn.h $(INCDIR)/ap_config_auto.h \
+ $(OSDIR)/os.h $(OSDIR)/os-inline.c $(INCDIR)/ap_ctype.h \
+ $(INCDIR)/httpd.h $(INCDIR)/ap_mm.h $(INCDIR)/ap_alloc.h \
+ $(INCDIR)/ap_hook.h $(INCDIR)/ap_ctx.h $(INCDIR)/buff.h \
+ $(INCDIR)/ap.h $(INCDIR)/util_uri.h \
+ $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \
+ $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \
+ $(INCDIR)/http_main.h $(INCDIR)/http_core.h \
+ $(INCDIR)/http_log.h $(INCDIR)/scoreboard.h \
+ $(INCDIR)/util_md5.h $(INCDIR)/ap_md5.h \
+ $(INCDIR)/fnmatch.h ssl_expr.h ssl_util_ssl.h ssl_util_table.h
+ssl_scache_shmcb.o ssl_scache_shmcb.lo: ssl_scache_shmcb.c mod_ssl.h \
+ $(INCDIR)/ap_config.h $(INCDIR)/ap_mmn.h \
+ $(INCDIR)/ap_config_auto.h $(OSDIR)/os.h \
+ $(OSDIR)/os-inline.c $(INCDIR)/ap_ctype.h \
+ $(INCDIR)/httpd.h $(INCDIR)/ap_mm.h $(INCDIR)/ap_alloc.h \
+ $(INCDIR)/ap_hook.h $(INCDIR)/ap_ctx.h $(INCDIR)/buff.h \
+ $(INCDIR)/ap.h $(INCDIR)/util_uri.h \
+ $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \
+ $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \
+ $(INCDIR)/http_main.h $(INCDIR)/http_core.h \
+ $(INCDIR)/http_log.h $(INCDIR)/scoreboard.h \
+ $(INCDIR)/util_md5.h $(INCDIR)/ap_md5.h \
+ $(INCDIR)/fnmatch.h ssl_expr.h ssl_util_ssl.h ssl_util_table.h
+ssl_scache_shmht.o ssl_scache_shmht.lo: ssl_scache_shmht.c mod_ssl.h \
+ $(INCDIR)/ap_config.h $(INCDIR)/ap_mmn.h \
+ $(INCDIR)/ap_config_auto.h $(OSDIR)/os.h \
+ $(OSDIR)/os-inline.c $(INCDIR)/ap_ctype.h \
+ $(INCDIR)/httpd.h $(INCDIR)/ap_mm.h $(INCDIR)/ap_alloc.h \
+ $(INCDIR)/ap_hook.h $(INCDIR)/ap_ctx.h $(INCDIR)/buff.h \
+ $(INCDIR)/ap.h $(INCDIR)/util_uri.h \
+ $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \
+ $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \
+ $(INCDIR)/http_main.h $(INCDIR)/http_core.h \
+ $(INCDIR)/http_log.h $(INCDIR)/scoreboard.h \
+ $(INCDIR)/util_md5.h $(INCDIR)/ap_md5.h \
+ $(INCDIR)/fnmatch.h ssl_expr.h ssl_util_ssl.h ssl_util_table.h
+ssl_util.o ssl_util.lo: ssl_util.c mod_ssl.h $(INCDIR)/ap_config.h \
+ $(INCDIR)/ap_mmn.h $(INCDIR)/ap_config_auto.h \
+ $(OSDIR)/os.h $(OSDIR)/os-inline.c $(INCDIR)/ap_ctype.h \
+ $(INCDIR)/httpd.h $(INCDIR)/ap_mm.h $(INCDIR)/ap_alloc.h \
+ $(INCDIR)/ap_hook.h $(INCDIR)/ap_ctx.h $(INCDIR)/buff.h \
+ $(INCDIR)/ap.h $(INCDIR)/util_uri.h \
+ $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \
+ $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \
+ $(INCDIR)/http_main.h $(INCDIR)/http_core.h \
+ $(INCDIR)/http_log.h $(INCDIR)/scoreboard.h \
+ $(INCDIR)/util_md5.h $(INCDIR)/ap_md5.h \
+ $(INCDIR)/fnmatch.h ssl_expr.h ssl_util_ssl.h ssl_util_table.h
+ssl_util_sdbm.o ssl_util_sdbm.lo: ssl_util_sdbm.c mod_ssl.h $(INCDIR)/ap_config.h \
+ $(INCDIR)/ap_mmn.h $(INCDIR)/ap_config_auto.h \
+ $(OSDIR)/os.h $(OSDIR)/os-inline.c $(INCDIR)/ap_ctype.h \
+ $(INCDIR)/httpd.h $(INCDIR)/ap_mm.h $(INCDIR)/ap_alloc.h \
+ $(INCDIR)/ap_hook.h $(INCDIR)/ap_ctx.h $(INCDIR)/buff.h \
+ $(INCDIR)/ap.h $(INCDIR)/util_uri.h \
+ $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \
+ $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \
+ $(INCDIR)/http_main.h $(INCDIR)/http_core.h \
+ $(INCDIR)/http_log.h $(INCDIR)/scoreboard.h \
+ $(INCDIR)/util_md5.h $(INCDIR)/ap_md5.h \
+ $(INCDIR)/fnmatch.h ssl_expr.h ssl_util_ssl.h ssl_util_table.h
+ssl_util_ssl.o ssl_util_ssl.lo: ssl_util_ssl.c mod_ssl.h $(INCDIR)/ap_config.h \
+ $(INCDIR)/ap_mmn.h $(INCDIR)/ap_config_auto.h \
+ $(OSDIR)/os.h $(OSDIR)/os-inline.c $(INCDIR)/ap_ctype.h \
+ $(INCDIR)/httpd.h $(INCDIR)/ap_mm.h $(INCDIR)/ap_alloc.h \
+ $(INCDIR)/ap_hook.h $(INCDIR)/ap_ctx.h $(INCDIR)/buff.h \
+ $(INCDIR)/ap.h $(INCDIR)/util_uri.h \
+ $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \
+ $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \
+ $(INCDIR)/http_main.h $(INCDIR)/http_core.h \
+ $(INCDIR)/http_log.h $(INCDIR)/scoreboard.h \
+ $(INCDIR)/util_md5.h $(INCDIR)/ap_md5.h \
+ $(INCDIR)/fnmatch.h ssl_expr.h ssl_util_ssl.h ssl_util_table.h
+ssl_util_table.o ssl_util_table.lo: ssl_util_table.c ssl_util_table.h
diff --git a/modules/ssl/Makefile.win32 b/modules/ssl/Makefile.win32
new file mode 100644 (file)
index 0000000..92781c1
--- /dev/null
@@ -0,0 +1,133 @@
+##                      _             _ 
+##  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
+## | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
+## | | | | | | (_) | (_| |   \__ \__ \ |  www.modssl.org
+## |_| |_| |_|\___/ \__,_|___|___/___/_|  ftp.modssl.org
+##                      |_____|         
+##  Makefile.win32
+##  Apache 1.3 Makefile for SSL module (Win32 environment)
+##
+
+##
+##  ====================================================================
+##  Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+## 
+##  Redistribution and use in source and binary forms, with or without
+##  modification, are permitted provided that the following conditions
+##  are met:
+## 
+##  1. Redistributions of source code must retain the above copyright
+##     notice, this list of conditions and the following disclaimer. 
+## 
+##  2. Redistributions in binary form must reproduce the above copyright
+##     notice, this list of conditions and the following
+##     disclaimer in the documentation and/or other materials
+##     provided with the distribution.
+## 
+##  3. All advertising materials mentioning features or use of this
+##     software must display the following acknowledgment:
+##     "This product includes software developed by 
+##      Ralf S. Engelschall <rse@engelschall.com> for use in the
+##      mod_ssl project (http://www.modssl.org/)."
+## 
+##  4. The names "mod_ssl" must not be used to endorse or promote
+##     products derived from this software without prior written
+##     permission. For written permission, please contact
+##     rse@engelschall.com.
+## 
+##  5. Products derived from this software may not be called "mod_ssl"
+##     nor may "mod_ssl" appear in their names without prior
+##     written permission of Ralf S. Engelschall.
+## 
+##  6. Redistributions of any form whatsoever must retain the following
+##     acknowledgment:
+##     "This product includes software developed by 
+##      Ralf S. Engelschall <rse@engelschall.com> for use in the
+##      mod_ssl project (http://www.modssl.org/)."
+## 
+##  THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+##  EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+##  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+##  PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+##  HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+##  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+##  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+##  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+##  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+##  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+##  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+##  OF THE POSSIBILITY OF SUCH DAMAGE.
+##  ====================================================================
+##
+
+#   the following variables are automatically
+#   adjusted by the configure.bat script.
+SSL_INC          = p:\ssl\work\win32\openssl\include
+SSL_LIB          = p:\ssl\work\win32\openssl\lib
+MOD_SSL_VERS_NUM = 000000
+MOD_SSL_VERS_STR = 0.0.0
+
+#   build tools and flags
+CC       = cl.exe
+CFLAGS   = /nologo /c /O2 /MD /W3 /GX /DNDEBUG /DWIN32 /D_WINDOWS /DSHARED_MODULE /DEAPI
+CFLAGS   = $(CFLAGS) /DMOD_SSL=$(MOD_SSL_VERS_NUM) /DMOD_SSL_VERSION=\"$(MOD_SSL_VERS_STR)\"
+CFLAGS   = $(CFLAGS) /I..\..\include /I..\..\os\win32 /I$(SSL_INC)
+LD       = link.exe
+LDFLAGS  = /nologo
+RM       = del
+
+#   name and extension of generated mod_ssl library file
+LIBNAME  = mod_ssl
+LIBEXT   = so
+LIBFILE  = $(LIBNAME).$(LIBEXT)
+
+#   mod_ssl object files
+OBJS=\
+ mod_ssl.obj\
+ ssl_engine_config.obj\
+ ssl_engine_compat.obj\
+ ssl_engine_ds.obj\
+ ssl_engine_dh.obj\
+ ssl_engine_init.obj\
+ ssl_engine_kernel.obj\
+ ssl_engine_rand.obj\
+ ssl_engine_io.obj\
+ ssl_engine_log.obj\
+ ssl_engine_mutex.obj\
+ ssl_engine_pphrase.obj\
+ ssl_engine_vars.obj\
+ ssl_engine_ext.obj\
+ ssl_scache.obj\
+ ssl_scache_dbm.obj\
+ ssl_scache_shmcb.obj\
+ ssl_scache_shmht.obj\
+ ssl_expr.obj\
+ ssl_expr_scan.obj\
+ ssl_expr_parse.obj\
+ ssl_expr_eval.obj\
+ ssl_util.obj\
+ ssl_util_ssl.obj\
+ ssl_util_sdbm.obj\
+ ssl_util_table.obj
+
+.c.obj:
+       $(CC) $(CFLAGS) $<
+
+all: $(LIBFILE)
+
+$(LIBNAME).lib: $(OBJS)
+       $(LD) $(LDFLAGS) /lib /out:$@ \
+               $(OBJS)
+
+$(LIBNAME).so: $(OBJS)
+       $(LD) $(LDFLAGS) /dll /out:$@ \
+               $(OBJS) \
+               ..\..\Release\ApacheCore.lib \
+               $(SSL_LIB)\ssleay32.lib \
+               $(SSL_LIB)\libeay32.lib \
+               wsock32.lib gdi32.lib
+
+clean:
+       -$(RM) $(LIBFILE)
+       -$(RM) $(OBJS)
+
diff --git a/modules/ssl/README b/modules/ssl/README
new file mode 100644 (file)
index 0000000..ca9e225
--- /dev/null
@@ -0,0 +1,163 @@
+                      _             _ 
+  _ __ ___   ___   __| |    ___ ___| |
+ | '_ ` _ \ / _ \ / _` |   / __/ __| | 
+ | | | | | | (_) | (_| |   \__ \__ \ | ``mod_ssl combines the flexibility of
+ |_| |_| |_|\___/ \__,_|___|___/___/_|   Apache with the security of OpenSSL.''
+                      |_____|                                           
+ mod_ssl                               ``Ralf Engelschall has released an
+ Apache Interface to OpenSSL             excellent module that integrates
+ http://www.modssl.org/                  Apache and SSLeay.''                 
+ Version 2.8                                               -- Tim J. Hudson
+
+ SYNOPSIS
+
+ This Apache module provides strong cryptography for the Apache 1.3 webserver
+ via the Secure Sockets Layer (SSL v2/v3) and Transport Layer Security (TLS
+ v1) protocols by the help of the SSL/TLS implementation library OpenSSL which
+ is based on SSLeay from Eric A. Young and Tim J. Hudson. The mod_ssl package
+ was created in April 1998 by Ralf S. Engelschall and was originally derived
+ from software developed by Ben Laurie for use in the Apache-SSL HTTP server
+ project. 
+
+ SOURCES
+
+ Here is a short overview of the source files:
+
+   Makefile.libdir ......... dummy for Apache config mechanism
+   Makefile.tmpl ........... Makefile template for Unix  platform
+   Makefile.win32 .......... Makefile template for Win32 platform
+   libssl.module ........... stub called from the Apache config mechanism
+   libssl.version .......... file containing the mod_ssl version information
+   mod_ssl.c ............... main source file containing API structures
+   mod_ssl.h ............... common header file of mod_ssl
+   ssl_engine_compat.c ..... backward compatibility support
+   ssl_engine_config.c ..... module configuration handling
+   ssl_engine_dh.c ......... DSA/DH support
+   ssl_engine_ds.c ......... data structures
+   ssl_engine_ext.c ........ Extensions to other Apache parts
+   ssl_engine_init.c ....... module initialization
+   ssl_engine_io.c ......... I/O support
+   ssl_engine_kernel.c ..... SSL engine kernel
+   ssl_engine_log.c ........ logfile support
+   ssl_engine_mutex.c ...... mutual exclusion support
+   ssl_engine_pphrase.c .... pass-phrase handling
+   ssl_engine_rand.c ....... PRNG support
+   ssl_engine_vars.c ....... Variable Expansion support
+   ssl_expr.c .............. expression handling main source
+   ssl_expr.h .............. expression handling common header
+   ssl_expr_scan.c ......... expression scanner automaton (pre-generated)
+   ssl_expr_scan.l ......... expression scanner source
+   ssl_expr_parse.c ........ expression parser automaton  (pre-generated)
+   ssl_expr_parse.h ........ expression parser header     (pre-generated)
+   ssl_expr_parse.y ........ expression parser source
+   ssl_expr_eval.c ......... expression machine evaluation
+   ssl_scache.c ............ session cache abstraction layer
+   ssl_scache_dbm.c ........ session cache via DBM file
+   ssl_scache_shmcb.c ...... session cache via shared memory cyclic buffer
+   ssl_scache_shmht.c ...... session cache via shared memory hash table
+   ssl_util.c .............. utility functions
+   ssl_util_ssl.c .......... the OpenSSL companion source
+   ssl_util_ssl.h .......... the OpenSSL companion header
+   ssl_util_sdbm.c ......... the SDBM library source
+   ssl_util_sdbm.h ......... the SDBM library header
+   ssl_util_table.c ........ the hash table library source
+   ssl_util_table.h ........ the hash table library header
+
+ The source files are written in clean ANSI C and pass the ``gcc -O -g
+ -ggdb3 -Wall -Wshadow -Wpointer-arith -Wcast-align -Wmissing-prototypes
+ -Wmissing-declarations -Wnested-externs -Winline'' compiler test
+ (assuming `gcc' is GCC 2.95.2 or newer) without any complains. When
+ you make changes or additions make sure the source still passes this
+ compiler test.
+
+ FUNCTIONS
+  
+ Inside the source code you will be confronted with the following types of
+ functions which can be identified by their prefixes:
+
+   ap_xxxx() ............... Apache API function
+   ssl_xxxx() .............. mod_ssl function
+   SSL_xxxx() .............. OpenSSL function (SSL library)
+   OpenSSL_xxxx() .......... OpenSSL function (SSL library)
+   X509_xxxx() ............. OpenSSL function (Crypto library)
+   PEM_xxxx() .............. OpenSSL function (Crypto library)
+   EVP_xxxx() .............. OpenSSL function (Crypto library)
+   RSA_xxxx() .............. OpenSSL function (Crypto library)
+
+ DATA STRUCTURES
+
+ Inside the source code you will be confronted with the following
+ data structures:
+
+   ap_ctx .................. Apache EAPI Context
+   server_rec .............. Apache (Virtual) Server
+   conn_rec ................ Apache Connection
+   BUFF .................... Apache Connection Buffer
+   request_rec ............. Apache Request
+   SSLModConfig ............ mod_ssl (Global)  Module Configuration
+   SSLSrvConfig ............ mod_ssl (Virtual) Server Configuration
+   SSLDirConfig ............ mod_ssl Directory Configuration
+   SSL_CTX ................. OpenSSL Context
+   SSL_METHOD .............. OpenSSL Protocol Method
+   SSL_CIPHER .............. OpenSSL Cipher
+   SSL_SESSION ............. OpenSSL Session
+   SSL ..................... OpenSSL Connection
+   BIO ..................... OpenSSL Connection Buffer
+
+ For an overview how these are related and chained together have a look at the
+ page in README.dsov.{fig,ps}. It contains overview diagrams for those data
+ structures. It's designed for DIN A4 paper size, but you can easily generate
+ a smaller version inside XFig by specifing a magnification on the Export
+ panel.
+
+ EXPERIMENTAL CODE
+
+ Experimental code is always encapsulated as following:
+
+   | #ifdef SSL_EXPERIMENTAL_xxxx
+   | ...
+   | #endif
+
+ This way it is only compiled in when this define is enabled with
+ the APACI --enable-rule=SSL_EXPERIMENTAL option and as long as the
+ C pre-processor variable SSL_EXPERIMENTAL_xxxx_IGNORE is _NOT_
+ defined (via CFLAGS). Or in other words: SSL_EXPERIMENTAL enables all
+ SSL_EXPERIMENTAL_xxxx variables, except if SSL_EXPERIMENTAL_xxxx_IGNORE
+ is already defined. Currently the following features are experimental:
+
+   o SSL_EXPERIMENTAL_PERDIRCA
+     The ability to use SSLCACertificateFile and SSLCACertificatePath
+     in a per-directory context (.htaccess). This is provided by some nasty
+     reconfiguration hacks until OpenSSL has better support for this. It
+     should work on non-multithreaded platforms (all but Win32).
+
+   o SSL_EXPERIMENTAL_PROXY
+     The ability to use various additional SSLProxyXXX directives in
+     oder to control extended client functionality in the HTTPS proxy
+     code.
+
+   o SSL_EXPERIMENTAL_ENGINE
+     The ability to support the new forthcoming OpenSSL ENGINE stuff.
+     Until this development branch of OpenSSL is merged into the main
+     stream, you have to use openssl-engine-0.9.x.tar.gz for this.
+     mod_ssl automatically recognizes this OpenSSL variant and then can
+     activate external crypto devices through SSLCryptoDevice directive.
+
+ VENDOR EXTENSIONS
+
+ Inside the mod_ssl sources you can enable various EAPI vendor hooks
+ (`ap::mod_ssl::vendor::xxxx') by using the APACI --enable-rule=SSL_VENDOR
+ option.  These hooks can be used to change or extend mod_ssl by a vendor
+ without patching the source code. Grep for `ap::mod_ssl::vendor::'.
+ Additionally vendors can add their own source code to files named
+ ssl_vendor.c, ssl_vendor_XXX.c, etc.  The libssl.module script automatically
+ picks these up under configuration time and mod_ssl under run-time calls the
+ functions `void ssl_vendor_register(void)' and `void
+ ssl_vendor_unregister(void)' inside these objects to bootstrap them.
+
+ An ssl_vendor.c should at least contain the following contents:
+
+   |  #include "mod_ssl.h"
+   |  void ssl_vendor_register(void) { return; }
+   |  void ssl_vendor_unregister(void) { return; }
+
diff --git a/modules/ssl/README.dsov.fig b/modules/ssl/README.dsov.fig
new file mode 100644 (file)
index 0000000..d8d03db
--- /dev/null
@@ -0,0 +1,346 @@
+#FIG 3.2
+Landscape
+Center
+Metric
+Letter  
+100.00
+Single
+-2
+1200 2
+0 32 #616561
+0 33 #b6b2b6
+0 34 #f7f3f7
+0 35 #cfcfcf
+0 36 #ffffff
+6 6345 2835 7155 3150
+6 6345 2970 7110 3150
+4 0 0 200 0 20 8 0.0000 4 120 585 6345 3105 "ssl_module")\001
+-6
+4 0 0 200 0 20 8 0.0000 4 120 660 6345 2970 ap_ctx_get(...,\001
+-6
+6 10800 2610 12240 3060
+4 0 0 200 0 20 8 0.0000 4 120 1170 10800 2745 ap_get_module_config(...\001
+4 0 0 200 0 20 8 0.0000 4 120 795 10800 2880 ->per_dir_config,\001
+4 0 0 200 0 20 8 0.0000 4 120 585 10800 3015 &ssl_module)\001
+-6
+6 7920 4770 9135 4995
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+        9135 4995 7920 4995 7920 4770 9135 4770 9135 4995
+4 0 0 100 0 18 12 0.0000 4 180 1065 8010 4950 request_rec\001
+-6
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        6975 3330 7425 2520
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        7200 4230 9450 2520
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        7875 4905 7200 5220
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        6750 5130 6750 4545
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        6705 5445 7155 6120
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        7875 4815 7200 4590
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        9585 2565 11475 4230
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        10170 5130 11835 4545
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        7920 6075 9855 5400
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        9990 5445 10935 5625
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        10215 5310 10935 5310
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        11925 4590 11925 5085
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        9810 5490 9810 6840
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        9945 5445 10935 6030
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        8865 4725 10800 2565
+2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2
+        675 6075 5850 6075
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        675 6525 675 6075
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+       1 0 1.00 60.00 120.00
+        5850 6075 5850 6525
+2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2
+        900 5625 5625 5625
+2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2
+        1125 5175 5400 5175
+2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2
+        1350 4725 5175 4725
+2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2
+        1575 4275 4950 4275
+2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2
+        1800 3825 4725 3825
+2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2
+        2025 3375 4500 3375
+2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2
+        2250 2925 4275 2925
+2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2
+        2475 2475 4050 2475
+2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2
+        2700 2025 3825 2025
+2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2
+        2925 1575 3600 1575
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        900 6075 900 5625
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        1125 6525 1125 5175
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        1350 5175 1350 4725
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        1575 4725 1575 4275
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        1800 6525 1800 3825
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        2025 3825 2025 3375
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        2250 3375 2250 2925
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        2475 2925 2475 2475
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+       1 0 1.00 60.00 120.00
+        5625 5625 5625 6075
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+       1 0 1.00 60.00 120.00
+        5400 5175 5400 6525
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+       1 0 1.00 60.00 120.00
+        5175 4725 5175 5175
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+       1 0 1.00 60.00 120.00
+        4950 4275 4950 4725
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+       1 0 1.00 60.00 120.00
+        4725 3825 4725 6525
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+       1 0 1.00 60.00 120.00
+        4500 3375 4500 3825
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+       1 0 1.00 60.00 120.00
+        4275 2925 4275 3375
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+       1 0 1.00 60.00 120.00
+        4050 2475 4050 2925
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        2700 6525 2700 2025
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+       1 0 1.00 60.00 120.00
+        3825 2025 3825 6525
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+       1 0 1.00 60.00 120.00
+        3600 1575 3600 2025
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        2925 2025 2925 1575
+2 1 0 4 0 0 200 0 20 0.000 0 0 -1 1 0 2
+       1 1 4.00 60.00 120.00
+        540 6525 6300 6525
+2 3 0 1 7 7 800 0 20 0.000 0 0 -1 0 0 9
+        675 6525 5850 6525 5850 6075 5625 6075 5625 5625 900 5625
+        900 6075 675 6075 675 6525
+2 3 0 1 34 34 700 0 20 0.000 0 0 -1 0 0 13
+        1125 6525 5355 6525 5400 5175 5175 5175 5175 4725 4950 4725
+        4950 4275 1575 4275 1575 4725 1350 4725 1350 5175 1125 5175
+        1125 6525
+2 3 0 1 35 35 500 0 20 0.000 0 0 -1 0 0 17
+        1800 6525 4725 6525 4725 3825 4500 3825 4500 3375 4275 3375
+        4275 2925 4050 2925 4050 2475 2475 2475 2475 2925 2250 2925
+        2250 3375 2025 3375 2025 3825 1800 3825 1800 6525
+2 3 0 1 33 33 400 0 20 0.000 0 0 -1 0 0 9
+        2700 6525 3825 6525 3825 2025 3600 2025 3600 1575 2925 1575
+        2925 2025 2700 2025 2700 6525
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 1 2
+       2 0 1.00 60.00 120.00
+       2 0 1.00 60.00 120.00
+        2700 6750 3825 6750
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 1 2
+       2 0 1.00 60.00 120.00
+       2 0 1.00 60.00 120.00
+        1125 7200 5400 7200
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 1 2
+       2 0 1.00 60.00 120.00
+       2 0 1.00 60.00 120.00
+        1800 6975 4725 6975
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 1 2
+       2 0 1.00 60.00 120.00
+       2 0 1.00 60.00 120.00
+        675 7425 5850 7425
+2 1 2 1 0 34 200 0 20 3.000 0 1 -1 0 0 2
+        675 6570 675 7650
+2 1 2 1 0 34 200 0 20 3.000 0 1 -1 0 0 2
+        1125 6570 1125 7650
+2 1 2 1 0 34 200 0 20 3.000 0 1 -1 0 0 2
+        1800 6570 1800 7650
+2 1 2 1 0 34 200 0 20 3.000 0 1 -1 0 0 2
+        2700 6570 2700 7650
+2 1 2 1 0 34 200 0 20 3.000 0 1 -1 0 0 2
+        3825 6570 3825 7650
+2 1 2 1 0 34 200 0 20 3.000 0 1 -1 0 0 2
+        4725 6570 4725 7650
+2 1 2 1 0 34 200 0 20 3.000 0 1 -1 0 0 2
+        5400 6570 5400 7650
+2 1 2 1 0 34 200 0 20 3.000 0 1 -1 0 0 2
+        5850 6570 5850 7650
+2 4 0 2 0 7 100 0 -1 0.000 0 0 20 0 0 5
+        12600 8550 450 8550 450 225 12600 225 12600 8550
+2 4 0 1 0 34 200 0 20 0.000 0 0 20 0 0 5
+        12600 1350 450 1350 450 225 12600 225 12600 1350
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+        10170 2475 8775 2475 8775 2250 10170 2250 10170 2475
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+        11925 2475 10575 2475 10575 2250 11925 2250 11925 2475
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+        12375 4500 11430 4500 11430 4275 12375 4275 12375 4500
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+        12375 5400 10980 5400 10980 5175 12375 5175 12375 5400
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+        10170 5400 9675 5400 9675 5175 10170 5175 10170 5400
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+        7875 6300 7200 6300 7200 6075 7875 6075 7875 6300
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+        8190 2475 6750 2475 6750 2250 8190 2250 8190 2475
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+        7605 3600 6300 3600 6300 3375 7605 3375 7605 3600
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+        7335 4500 6300 4500 6300 4275 7335 4275 7335 4500
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+        7200 5400 6300 5400 6300 5175 7200 5175 7200 5400
+2 1 0 6 7 7 600 0 -1 0.000 0 0 -1 0 0 2
+        9450 4500 6075 1935
+2 1 0 6 7 7 600 0 -1 0.000 0 0 4 0 0 2
+        9450 4500 12465 2205
+2 1 0 6 7 7 600 0 -1 0.000 0 0 4 0 0 2
+        9450 4500 9450 7785
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        9630 5310 7245 5310
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        11385 4365 7380 4365
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+        12240 5805 10980 5805 10980 5580 12240 5580 12240 5805
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+        12375 6210 10980 6210 10980 5985 12375 5985 12375 6210
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        11205 6885 9900 5445
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+        12285 7155 10530 7155 10530 6930 12285 6930 12285 7155
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+        10170 7155 9630 7155 9630 6930 10170 6930 10170 7155
+2 1 0 6 7 7 600 0 -1 0.000 0 0 4 0 0 2
+        12510 6435 9450 6435
+2 1 0 1 0 34 300 0 20 0.000 0 0 7 1 0 4
+       1 1 1.00 60.00 120.00
+        12375 4455 12510 4635 12510 6210 11970 6885
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        9850 5143 9175 4918
+3 1 0 1 34 34 800 0 20 0.000 0 0 0 41
+        7380 1710 6390 2115 5535 2115 6075 3015 5670 3465 6165 3915
+        5715 4410 6030 5040 6030 5310 6480 5715 6390 6255 6975 6300
+        7065 6975 7965 6750 8100 7560 8955 7290 9360 7740 9720 7560
+        10755 8145 12060 8280 12375 7650 12420 7200 12510 7065 12330 6660
+        12510 6390 12420 5940 12375 5400 12510 5220 12510 4725 12600 4275
+        12375 3645 12105 3240 12150 2745 12375 2700 12330 1980 11790 1575
+        11250 1935 10125 1485 8955 2070 7785 1620 7695 1575
+        1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000
+        1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000
+        1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000
+        1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000
+        1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000
+        1.000
+4 0 0 100 0 0 12 0.0000 4 180 1440 10575 675 Ralf S. Engelschall\001
+4 0 0 100 0 18 20 0.0000 4 270 3840 4275 675 Apache+mod_ssl+OpenSSL\001
+4 0 0 100 0 0 10 0.0000 4 135 1320 10575 855 rse@engelschall.com\001
+4 0 0 100 0 0 10 0.0000 4 135 1410 10575 1035 www.engelschall.com\001
+4 0 0 100 0 0 12 0.0000 4 135 870 900 675 Version 1.3\001
+4 0 0 100 0 0 12 0.0000 4 180 1035 900 855 12-Apr-1999\001
+4 0 0 200 0 20 8 0.0000 4 60 390 6210 4680 ->server\001
+4 0 0 200 0 20 8 0.0000 4 120 855 8280 6120 ap_ctx_get(...,"ssl")\001
+4 0 0 200 0 20 8 0.0000 4 120 1170 7740 2700 ap_get_module_config(...\001
+4 0 0 200 0 20 8 0.0000 4 120 810 7740 2835 ->module_config,\001
+4 0 0 200 0 20 8 0.0000 4 120 585 7740 2970 &ssl_module)\001
+4 0 0 100 0 18 20 0.0000 4 270 1200 9000 8100 Chaining\001
+4 0 0 100 0 18 20 0.0000 4 210 1095 2745 8100 Lifetime\001
+4 0 0 100 0 18 12 0.0000 4 180 1215 810 6255 ap_global_ctx\001
+4 0 0 100 0 18 12 0.0000 4 180 1305 990 5805 SSLModConfig\001
+4 0 0 100 0 18 12 0.0000 4 180 840 4050 4455 SSL_CTX\001
+4 0 0 100 0 18 12 0.0000 4 150 975 4455 5355 server_rec\001
+4 0 0 100 0 18 12 0.0000 4 180 1260 3870 4905 SSLSrvConfig\001
+4 0 0 100 0 18 12 0.0000 4 135 480 1845 4005 BUFF\001
+4 0 0 100 0 18 12 0.0000 4 150 810 2070 3555 conn_rec\001
+4 0 0 100 0 18 12 0.0000 4 135 345 2295 3105 BIO\001
+4 0 0 100 0 18 12 0.0000 4 135 375 2565 2655 SSL\001
+4 0 0 100 0 18 12 0.0000 4 180 1185 3645 1620 SSLDirConfig\001
+4 0 0 100 0 18 12 0.0000 4 180 1065 3915 2070 request_rec\001
+4 0 0 200 0 0 8 0.0000 4 120 1440 900 7560 Startup, Runtime, Shutdown\001
+4 0 0 200 0 0 8 0.0000 4 105 975 1350 7335 Configuration Time\001
+4 0 0 200 0 0 8 0.0000 4 90 1050 2025 7110 Connection Duration\001
+4 0 0 200 0 0 8 0.0000 4 120 885 2835 6885 Request Duration\001
+4 0 0 200 0 18 20 0.0000 4 195 90 6345 6795 t\001
+4 0 0 200 0 20 8 0.0000 4 90 345 7110 5985 ->client\001
+4 0 0 100 0 18 12 0.0000 4 180 1305 6795 2430 SSLModConfig\001
+4 0 0 100 0 18 12 0.0000 4 180 1260 8865 2430 SSLSrvConfig\001
+4 0 0 100 0 18 12 0.0000 4 180 1215 6345 3555 ap_global_ctx\001
+4 0 0 100 0 18 12 0.0000 4 150 975 6345 4455 server_rec\001
+4 0 0 100 0 18 12 0.0000 4 150 810 6345 5355 conn_rec\001
+4 0 0 100 0 18 12 0.0000 4 135 375 9720 5355 SSL\001
+4 0 0 100 0 18 12 0.0000 4 180 1185 10665 2430 SSLDirConfig\001
+4 0 0 100 0 18 12 0.0000 4 135 480 7290 6255 BUFF\001
+4 0 0 100 0 18 12 0.0000 4 180 1305 11025 5355 SSL_METHOD\001
+4 0 0 100 0 18 12 0.0000 4 180 840 11475 4455 SSL_CTX\001
+4 0 0 100 0 18 24 0.0000 4 285 4365 3915 1080 Data Structure Overview\001
+4 0 0 200 0 20 8 0.0000 4 90 615 7065 5085 ->connection\001
+4 0 0 200 0 20 8 0.0000 4 60 390 7065 4770 ->server\001
+4 0 0 200 0 20 8 0.0000 4 120 960 8010 5445 SSL_get_app_data()\001
+4 0 0 200 0 20 8 0.0000 4 120 510 10530 4050 ->pSSLCtx\001
+4 0 0 200 0 20 8 0.0000 4 120 1215 7875 4275 SSL_CTX_get_app_data()\001
+4 0 0 200 0 20 8 0.0000 4 120 1155 10305 5535 SSL_get_current_cipher()\001
+4 0 0 100 0 18 12 0.0000 4 180 1170 11025 5760 SSL_CIPHER\001
+4 0 0 100 0 18 12 0.0000 4 180 1350 10980 6165 SSL_SESSION\001
+4 0 0 200 0 20 8 0.0000 4 120 840 10440 5940 SSL_get_session()\001
+4 0 0 100 0 18 12 0.0000 4 180 1665 10575 7110 X509_STORE_CTX\001
+4 0 0 100 0 18 12 0.0000 4 135 345 9720 7110 BIO\001
+4 0 0 200 0 20 8 0.0000 4 120 840 9540 7335 SSL_get_{r,w}bio()\001
+4 0 0 100 0 18 20 0.0000 4 270 1170 8730 3465 mod_ssl\001
+4 0 0 100 0 18 20 0.0000 4 270 1050 8145 6750 Apache\001
+4 0 0 200 0 20 8 0.0000 4 120 945 10125 4680 SSL_get_SSL_CTX()\001
+4 0 0 200 0 20 8 0.0000 4 120 1170 10350 5175 SSL_get_SSL_METHOD()\001
+4 0 0 200 0 20 8 0.0000 4 90 465 11745 4770 ->method\001
+4 0 0 200 0 20 8 0.0000 4 120 1665 9945 6480 X509_STORE_CTX_get_app_data()\001
+4 0 0 200 0 20 8 0.0000 4 120 1215 10980 6705 SSL_CTX_get_cert_store()\001
+4 0 0 200 0 20 8 0.0000 4 120 1020 8280 5130 SSL_get_app_data2()\001
+4 0 0 100 0 18 20 0.0000 4 270 1290 10710 7605 OpenSSL\001
+4 0 0 100 0 18 12 0.0000 4 180 720 10710 7785 [Crypto]\001
+4 0 0 100 0 18 20 0.0000 4 270 1290 10935 3645 OpenSSL\001
+4 0 0 100 0 18 12 0.0000 4 180 495 10935 3825 [SSL]\001
diff --git a/modules/ssl/README.dsov.ps b/modules/ssl/README.dsov.ps
new file mode 100644 (file)
index 0000000..def19db
--- /dev/null
@@ -0,0 +1,1138 @@
+%!PS-Adobe-2.0
+%%Title: README.dsov.ps
+%%Creator: fig2dev Version 3.2 Patchlevel 1
+%%CreationDate: Mon Apr 12 17:09:11 1999
+%%For: rse@en1.engelschall.com (Ralf S. Engelschall)
+%%Orientation: Landscape
+%%BoundingBox: 59 37 553 755
+%%Pages: 1
+%%BeginSetup
+%%IncludeFeature: *PageSize Letter
+%%EndSetup
+%%Magnification: 0.9340
+%%EndComments
+/$F2psDict 200 dict def
+$F2psDict begin
+$F2psDict /mtrx matrix put
+/col-1 {0 setgray} bind def
+/col0 {0.000 0.000 0.000 srgb} bind def
+/col1 {0.000 0.000 1.000 srgb} bind def
+/col2 {0.000 1.000 0.000 srgb} bind def
+/col3 {0.000 1.000 1.000 srgb} bind def
+/col4 {1.000 0.000 0.000 srgb} bind def
+/col5 {1.000 0.000 1.000 srgb} bind def
+/col6 {1.000 1.000 0.000 srgb} bind def
+/col7 {1.000 1.000 1.000 srgb} bind def
+/col8 {0.000 0.000 0.560 srgb} bind def
+/col9 {0.000 0.000 0.690 srgb} bind def
+/col10 {0.000 0.000 0.820 srgb} bind def
+/col11 {0.530 0.810 1.000 srgb} bind def
+/col12 {0.000 0.560 0.000 srgb} bind def
+/col13 {0.000 0.690 0.000 srgb} bind def
+/col14 {0.000 0.820 0.000 srgb} bind def
+/col15 {0.000 0.560 0.560 srgb} bind def
+/col16 {0.000 0.690 0.690 srgb} bind def
+/col17 {0.000 0.820 0.820 srgb} bind def
+/col18 {0.560 0.000 0.000 srgb} bind def
+/col19 {0.690 0.000 0.000 srgb} bind def
+/col20 {0.820 0.000 0.000 srgb} bind def
+/col21 {0.560 0.000 0.560 srgb} bind def
+/col22 {0.690 0.000 0.690 srgb} bind def
+/col23 {0.820 0.000 0.820 srgb} bind def
+/col24 {0.500 0.190 0.000 srgb} bind def
+/col25 {0.630 0.250 0.000 srgb} bind def
+/col26 {0.750 0.380 0.000 srgb} bind def
+/col27 {1.000 0.500 0.500 srgb} bind def
+/col28 {1.000 0.630 0.630 srgb} bind def
+/col29 {1.000 0.750 0.750 srgb} bind def
+/col30 {1.000 0.880 0.880 srgb} bind def
+/col31 {1.000 0.840 0.000 srgb} bind def
+/col32 {0.380 0.396 0.380 srgb} bind def
+/col33 {0.714 0.698 0.714 srgb} bind def
+/col34 {0.969 0.953 0.969 srgb} bind def
+/col35 {0.812 0.812 0.812 srgb} bind def
+/col36 {1.000 1.000 1.000 srgb} bind def
+
+end
+save
+48.0 12.0 translate
+ 90 rotate
+1 -1 scale
+
+/cp {closepath} bind def
+/ef {eofill} bind def
+/gr {grestore} bind def
+/gs {gsave} bind def
+/sa {save} bind def
+/rs {restore} bind def
+/l {lineto} bind def
+/m {moveto} bind def
+/rm {rmoveto} bind def
+/n {newpath} bind def
+/s {stroke} bind def
+/sh {show} bind def
+/slc {setlinecap} bind def
+/slj {setlinejoin} bind def
+/slw {setlinewidth} bind def
+/srgb {setrgbcolor} bind def
+/rot {rotate} bind def
+/sc {scale} bind def
+/sd {setdash} bind def
+/ff {findfont} bind def
+/sf {setfont} bind def
+/scf {scalefont} bind def
+/sw {stringwidth} bind def
+/tr {translate} bind def
+/tnt {dup dup currentrgbcolor
+  4 -2 roll dup 1 exch sub 3 -1 roll mul add
+  4 -2 roll dup 1 exch sub 3 -1 roll mul add
+  4 -2 roll dup 1 exch sub 3 -1 roll mul add srgb}
+  bind def
+/shd {dup dup currentrgbcolor 4 -2 roll mul 4 -2 roll mul
+  4 -2 roll mul srgb} bind def
+/reencdict 12 dict def /ReEncode { reencdict begin
+/newcodesandnames exch def /newfontname exch def /basefontname exch def
+/basefontdict basefontname findfont def /newfont basefontdict maxlength dict def
+basefontdict { exch dup /FID ne { dup /Encoding eq
+{ exch dup length array copy newfont 3 1 roll put }
+{ exch newfont 3 1 roll put } ifelse } { pop pop } ifelse } forall
+newfont /FontName newfontname put newcodesandnames aload pop
+128 1 255 { newfont /Encoding get exch /.notdef put } for
+newcodesandnames length 2 idiv { newfont /Encoding get 3 1 roll put } repeat
+newfontname newfont definefont pop end } def
+/isovec [
+8#200 /grave 8#201 /acute 8#202 /circumflex 8#203 /tilde
+8#204 /macron 8#205 /breve 8#206 /dotaccent 8#207 /dieresis
+8#210 /ring 8#211 /cedilla 8#212 /hungarumlaut 8#213 /ogonek 8#214 /caron
+8#220 /dotlessi 8#230 /oe 8#231 /OE
+8#240 /space 8#241 /exclamdown 8#242 /cent 8#243 /sterling
+8#244 /currency 8#245 /yen 8#246 /brokenbar 8#247 /section 8#250 /dieresis
+8#251 /copyright 8#252 /ordfeminine 8#253 /guillemotleft 8#254 /logicalnot
+8#255 /endash 8#256 /registered 8#257 /macron 8#260 /degree 8#261 /plusminus
+8#262 /twosuperior 8#263 /threesuperior 8#264 /acute 8#265 /mu 8#266 /paragraph
+8#267 /periodcentered 8#270 /cedilla 8#271 /onesuperior 8#272 /ordmasculine
+8#273 /guillemotright 8#274 /onequarter 8#275 /onehalf
+8#276 /threequarters 8#277 /questiondown 8#300 /Agrave 8#301 /Aacute
+8#302 /Acircumflex 8#303 /Atilde 8#304 /Adieresis 8#305 /Aring
+8#306 /AE 8#307 /Ccedilla 8#310 /Egrave 8#311 /Eacute
+8#312 /Ecircumflex 8#313 /Edieresis 8#314 /Igrave 8#315 /Iacute
+8#316 /Icircumflex 8#317 /Idieresis 8#320 /Eth 8#321 /Ntilde 8#322 /Ograve
+8#323 /Oacute 8#324 /Ocircumflex 8#325 /Otilde 8#326 /Odieresis 8#327 /multiply
+8#330 /Oslash 8#331 /Ugrave 8#332 /Uacute 8#333 /Ucircumflex
+8#334 /Udieresis 8#335 /Yacute 8#336 /Thorn 8#337 /germandbls 8#340 /agrave
+8#341 /aacute 8#342 /acircumflex 8#343 /atilde 8#344 /adieresis 8#345 /aring
+8#346 /ae 8#347 /ccedilla 8#350 /egrave 8#351 /eacute
+8#352 /ecircumflex 8#353 /edieresis 8#354 /igrave 8#355 /iacute
+8#356 /icircumflex 8#357 /idieresis 8#360 /eth 8#361 /ntilde 8#362 /ograve
+8#363 /oacute 8#364 /ocircumflex 8#365 /otilde 8#366 /odieresis 8#367 /divide
+8#370 /oslash 8#371 /ugrave 8#372 /uacute 8#373 /ucircumflex
+8#374 /udieresis 8#375 /yacute 8#376 /thorn 8#377 /ydieresis] def
+/Times-Roman /Times-Roman-iso isovec ReEncode
+/Helvetica-Bold /Helvetica-Bold-iso isovec ReEncode
+/Helvetica-Narrow /Helvetica-Narrow-iso isovec ReEncode
+/$F2psBegin {$F2psDict begin /$F2psEnteredState save def} def
+/$F2psEnd {$F2psEnteredState restore end} def
+%%EndProlog
+
+$F2psBegin
+10 setmiterlimit
+n -1000 9572 m -1000 -1000 l 13622 -1000 l 13622 9572 l cp clip
+ 0.05883 0.05883 sc
+%%Page: 1 1
+% Polyline
+7.500 slw
+n 6413 2048 m 6380 2054 l 6348 2061 l 6315 2067 l 6283 2073 l 6250 2079 l
+ 6217 2084 l 6185 2090 l 6152 2095 l 6120 2101 l 6088 2107 l
+ 6057 2113 l 6027 2120 l 5998 2126 l 5970 2134 l 5943 2141 l
+ 5918 2149 l 5894 2158 l 5873 2167 l 5853 2177 l 5835 2187 l
+ 5819 2198 l 5805 2210 l 5793 2222 l 5782 2235 l 5774 2250 l
+ 5768 2265 l 5763 2281 l 5760 2299 l 5759 2318 l 5759 2339 l
+ 5761 2360 l 5764 2383 l 5768 2408 l 5774 2433 l 5780 2460 l
+ 5788 2488 l 5797 2516 l 5806 2546 l 5815 2575 l 5825 2606 l
+ 5836 2636 l 5846 2666 l 5856 2696 l 5866 2726 l 5875 2755 l
+ 5884 2784 l 5892 2812 l 5899 2839 l 5905 2866 l 5910 2891 l
+ 5915 2916 l 5918 2940 l 5919 2968 l 5920 2995 l 5919 3022 l
+ 5916 3048 l 5912 3075 l 5908 3101 l 5902 3127 l 5895 3153 l
+ 5887 3179 l 5880 3205 l 5871 3230 l 5863 3254 l 5855 3278 l
+ 5848 3302 l 5841 3324 l 5834 3346 l 5829 3367 l 5824 3388 l
+ 5821 3408 l 5819 3427 l 5819 3446 l 5820 3465 l 5823 3484 l
+ 5827 3503 l 5833 3522 l 5840 3542 l 5848 3562 l 5858 3582 l
+ 5868 3603 l 5880 3625 l 5891 3647 l 5904 3669 l 5916 3691 l
+ 5929 3713 l 5941 3736 l 5953 3758 l 5964 3779 l 5974 3801 l
+ 5983 3822 l 5991 3843 l 5997 3863 l 6002 3883 l 6006 3903 l
+ 6008 3923 l 6008 3942 l 6006 3962 l 6003 3983 l 5998 4004 l
+ 5992 4025 l 5985 4048 l 5977 4070 l 5968 4094 l 5958 4118 l
+ 5947 4142 l 5936 4167 l 5925 4192 l 5913 4216 l 5902 4241 l
+ 5892 4266 l 5882 4291 l 5872 4315 l 5864 4339 l 5857 4362 l
+ 5851 4386 l 5846 4409 l 5843 4433 l 5840 4456 l 5840 4480 l
+ 5840 4505 l 5842 4530 l 5845 4556 l 5849 4582 l 5854 4609 l
+ 5860 4636 l 5867 4664 l 5875 4692 l 5883 4720 l 5892 4747 l
+ 5901 4774 l 5910 4801 l 5920 4827 l 5929 4852 l 5938 4875 l
+ 5947 4898 l 5955 4920 l 5963 4941 l 5971 4961 l 5978 4980 l
+ 5985 5002 l 5992 5024 l 5999 5046 l 6005 5067 l 6010 5088 l
+ 6016 5109 l 6022 5129 l 6027 5150 l 6033 5170 l 6039 5190 l
+ 6045 5209 l 6052 5228 l 6059 5246 l 6067 5264 l 6075 5281 l
+ 6084 5298 l 6094 5315 l 6105 5333 l 6115 5347 l 6125 5361 l
+ 6137 5376 l 6149 5392 l 6162 5408 l 6176 5425 l 6191 5443 l
+ 6206 5461 l 6221 5480 l 6237 5499 l 6253 5519 l 6269 5539 l
+ 6284 5559 l 6299 5579 l 6313 5599 l 6327 5619 l 6340 5639 l
+ 6352 5659 l 6363 5679 l 6373 5698 l 6382 5718 l 6390 5738 l
+ 6398 5759 l 6404 5782 l 6410 5805 l 6415 5828 l 6420 5852 l
+ 6424 5877 l 6428 5902 l 6431 5927 l 6435 5952 l 6438 5977 l
+ 6442 6001 l 6446 6025 l 6450 6048 l 6455 6069 l 6461 6090 l
+ 6467 6109 l 6474 6127 l 6483 6143 l 6492 6159 l 6503 6173 l
+ 6515 6185 l 6528 6197 l 6543 6209 l 6560 6220 l 6578 6230 l
+ 6598 6240 l 6619 6250 l 6641 6260 l 6663 6270 l 6687 6281 l
+ 6710 6291 l 6733 6302 l 6757 6312 l 6779 6324 l 6801 6335 l
+ 6821 6348 l 6841 6361 l 6859 6374 l 6876 6389 l 6893 6405 l
+ 6906 6421 l 6919 6437 l 6932 6455 l 6944 6475 l 6955 6495 l
+ 6967 6516 l 6979 6538 l 6991 6561 l 7003 6584 l 7015 6608 l
+ 7027 6631 l 7040 6654 l 7053 6677 l 7067 6699 l 7081 6720 l
+ 7096 6739 l 7111 6758 l 7127 6774 l 7144 6789 l 7161 6803 l
+ 7180 6815 l 7200 6825 l 7220 6833 l 7240 6840 l 7263 6845 l
+ 7286 6850 l 7311 6854 l 7338 6857 l 7365 6859 l 7394 6861 l
+ 7424 6862 l 7454 6864 l 7485 6865 l 7516 6866 l 7547 6867 l
+ 7578 6868 l 7609 6870 l 7639 6872 l 7668 6875 l 7696 6879 l
+ 7723 6883 l 7748 6889 l 7773 6895 l 7795 6903 l 7817 6912 l
+ 7838 6923 l 7857 6934 l 7875 6948 l 7892 6963 l 7909 6980 l
+ 7926 6998 l 7941 7017 l 7957 7038 l 7972 7060 l 7987 7083 l
+ 8002 7106 l 8017 7130 l 8031 7154 l 8046 7178 l 8061 7202 l
+ 8075 7225 l 8090 7247 l 8105 7269 l 8120 7289 l 8135 7308 l
+ 8151 7326 l 8167 7342 l 8184 7356 l 8202 7369 l 8220 7380 l
+ 8239 7390 l 8260 7397 l 8282 7404 l 8305 7409 l 8330 7413 l
+ 8356 7416 l 8383 7418 l 8412 7420 l 8441 7420 l 8471 7419 l
+ 8502 7418 l 8534 7417 l 8565 7415 l 8597 7413 l 8629 7411 l
+ 8660 7409 l 8690 7407 l 8720 7405 l 8749 7404 l 8777 7404 l
+ 8804 7404 l 8830 7405 l 8856 7407 l 8880 7410 l 8906 7414 l
+ 8931 7420 l 8956 7427 l 8981 7435 l 9005 7444 l 9029 7455 l
+ 9053 7466 l 9077 7478 l 9100 7491 l 9123 7504 l 9146 7517 l
+ 9168 7531 l 9190 7544 l 9210 7557 l 9230 7570 l 9250 7582 l
+ 9268 7593 l 9286 7604 l 9304 7613 l 9320 7621 l 9336 7629 l
+ 9353 7635 l 9370 7641 l 9388 7645 l 9406 7648 l 9425 7650 l
+ 9444 7652 l 9464 7653 l 9485 7653 l 9508 7653 l 9531 7653 l
+ 9555 7653 l 9579 7653 l 9605 7654 l 9631 7655 l 9658 7656 l
+ 9685 7659 l 9713 7662 l 9742 7666 l 9771 7672 l 9801 7679 l
+ 9833 7688 l 9853 7694 l 9874 7700 l 9895 7708 l 9918 7716 l
+ 9941 7725 l 9966 7734 l 9991 7745 l 10017 7755 l 10045 7767 l
+ 10073 7779 l 10102 7791 l 10132 7804 l 10163 7818 l 10194 7831 l
+ 10227 7845 l 10259 7860 l 10293 7874 l 10326 7889 l 10360 7903 l
+ 10394 7918 l 10429 7932 l 10463 7947 l 10497 7961 l 10531 7974 l
+ 10565 7988 l 10599 8001 l 10633 8013 l 10667 8025 l 10700 8037 l
+ 10733 8049 l 10767 8059 l 10800 8070 l 10834 8080 l 10868 8090 l
+ 10902 8099 l 10937 8108 l 10973 8117 l 11009 8125 l 11045 8133 l
+ 11083 8141 l 11120 8148 l 11158 8155 l 11197 8161 l 11236 8167 l
+ 11275 8172 l 11313 8177 l 11352 8181 l 11391 8184 l 11429 8187 l
+ 11467 8190 l 11504 8191 l 11540 8192 l 11576 8192 l 11610 8192 l
+ 11644 8191 l 11676 8189 l 11707 8187 l 11738 8184 l 11767 8180 l
+ 11794 8176 l 11821 8171 l 11847 8165 l 11871 8159 l 11895 8153 l
+ 11923 8143 l 11950 8133 l 11976 8122 l 12001 8109 l 12025 8096 l
+ 12048 8081 l 12071 8065 l 12092 8048 l 12113 8031 l 12133 8012 l
+ 12153 7992 l 12171 7972 l 12188 7951 l 12205 7930 l 12220 7909 l
+ 12235 7887 l 12248 7865 l 12260 7843 l 12272 7822 l 12282 7800 l
+ 12292 7779 l 12301 7759 l 12309 7739 l 12316 7719 l 12323 7699 l
+ 12330 7680 l 12338 7655 l 12345 7631 l 12352 7607 l 12359 7582 l
+ 12365 7558 l 12371 7533 l 12377 7508 l 12382 7484 l 12388 7460 l
+ 12392 7436 l 12397 7414 l 12401 7391 l 12405 7370 l 12409 7350 l
+ 12412 7331 l 12415 7313 l 12418 7297 l 12421 7281 l 12424 7266 l
+ 12428 7253 l 12432 7234 l 12437 7216 l 12442 7199 l 12446 7183 l
+ 12451 7166 l 12456 7150 l 12460 7134 l 12463 7117 l 12466 7101 l
+ 12468 7086 l 12469 7070 l 12469 7054 l 12467 7037 l 12465 7020 l
+ 12462 7006 l 12459 6991 l 12455 6975 l 12450 6958 l 12445 6940 l
+ 12440 6921 l 12434 6901 l 12428 6880 l 12422 6859 l 12416 6838 l
+ 12411 6817 l 12406 6796 l 12401 6776 l 12397 6756 l 12394 6736 l
+ 12392 6718 l 12390 6700 l 12390 6683 l 12390 6665 l 12392 6649 l
+ 12394 6631 l 12397 6614 l 12401 6597 l 12406 6579 l 12411 6561 l
+ 12416 6542 l 12422 6524 l 12428 6505 l 12434 6487 l 12440 6468 l
+ 12445 6450 l 12450 6432 l 12455 6414 l 12459 6396 l 12462 6378 l
+ 12465 6360 l 12467 6343 l 12468 6326 l 12469 6308 l 12469 6289 l
+ 12468 6269 l 12468 6249 l 12466 6227 l 12464 6205 l 12462 6182 l
+ 12460 6159 l 12457 6135 l 12454 6111 l 12451 6087 l 12447 6063 l
+ 12444 6040 l 12441 6016 l 12437 5993 l 12434 5970 l 12431 5948 l
+ 12428 5925 l 12424 5902 l 12421 5879 l 12419 5855 l 12416 5831 l
+ 12413 5806 l 12411 5781 l 12408 5755 l 12406 5729 l 12404 5702 l
+ 12403 5676 l 12401 5651 l 12400 5625 l 12400 5601 l 12399 5578 l
+ 12399 5555 l 12400 5534 l 12401 5514 l 12402 5495 l 12403 5477 l
+ 12405 5460 l 12408 5440 l 12411 5421 l 12416 5402 l 12420 5384 l
+ 12426 5365 l 12431 5347 l 12437 5329 l 12444 5311 l 12450 5293 l
+ 12456 5275 l 12462 5258 l 12468 5240 l 12474 5222 l 12479 5205 l
+ 12483 5186 l 12488 5168 l 12490 5152 l 12493 5135 l 12496 5117 l
+ 12498 5099 l 12500 5079 l 12502 5058 l 12504 5036 l 12506 5014 l
+ 12507 4990 l 12509 4966 l 12510 4942 l 12512 4918 l 12513 4893 l
+ 12515 4869 l 12516 4845 l 12518 4822 l 12520 4799 l 12521 4776 l
+ 12523 4754 l 12525 4733 l 12527 4713 l 12529 4693 l 12531 4673 l
+ 12534 4653 l 12536 4632 l 12539 4610 l 12541 4588 l 12543 4566 l
+ 12546 4543 l 12548 4520 l 12550 4497 l 12552 4473 l 12553 4450 l
+ 12554 4426 l 12555 4403 l 12555 4380 l 12555 4357 l 12555 4334 l
+ 12554 4312 l 12552 4290 l 12550 4267 l 12548 4245 l 12545 4224 l
+ 12541 4203 l 12537 4181 l 12533 4159 l 12528 4136 l 12523 4112 l
+ 12517 4088 l 12510 4064 l 12503 4038 l 12496 4013 l 12488 3987 l
+ 12479 3961 l 12471 3935 l 12462 3909 l 12452 3884 l 12443 3859 l
+ 12434 3835 l 12424 3811 l 12415 3788 l 12405 3766 l 12396 3744 l
+ 12386 3723 l 12377 3702 l 12368 3683 l 12357 3661 l 12347 3640 l
+ 12336 3619 l 12325 3598 l 12314 3576 l 12303 3555 l 12291 3533 l
+ 12280 3511 l 12269 3489 l 12257 3467 l 12246 3446 l 12235 3424 l
+ 12225 3402 l 12215 3381 l 12206 3360 l 12197 3340 l 12189 3320 l
+ 12181 3301 l 12174 3281 l 12168 3262 l 12162 3244 l 12158 3225 l
+ 12153 3204 l 12149 3183 l 12145 3162 l 12142 3139 l 12140 3117 l
+ 12138 3094 l 12137 3071 l 12137 3047 l 12138 3024 l 12139 3001 l
+ 12141 2978 l 12143 2956 l 12146 2935 l 12150 2915 l 12154 2896 l
+ 12158 2879 l 12163 2862 l 12168 2847 l 12174 2833 l 12180 2820 l
+ 12188 2805 l 12197 2792 l 12206 2779 l 12216 2766 l 12227 2754 l
+ 12238 2742 l 12249 2730 l 12260 2717 l 12272 2704 l 12282 2691 l
+ 12292 2676 l 12302 2661 l 12310 2645 l 12318 2627 l 12324 2608 l
+ 12330 2588 l 12334 2571 l 12336 2553 l 12339 2534 l 12341 2513 l
+ 12342 2491 l 12343 2467 l 12343 2442 l 12342 2416 l 12340 2389 l
+ 12338 2360 l 12335 2332 l 12331 2303 l 12326 2273 l 12320 2244 l
+ 12314 2215 l 12307 2187 l 12299 2159 l 12290 2132 l 12280 2106 l
+ 12270 2081 l 12259 2056 l 12248 2033 l 12236 2011 l 12224 1990 l
+ 12210 1970 l 12196 1949 l 12181 1929 l 12164 1910 l 12147 1890 l
+ 12129 1871 l 12110 1853 l 12090 1835 l 12070 1818 l 12049 1802 l
+ 12027 1787 l 12005 1773 l 11983 1761 l 11961 1749 l 11939 1739 l
+ 11917 1730 l 11895 1722 l 11874 1716 l 11852 1710 l 11831 1707 l
+ 11811 1704 l 11790 1703 l 11769 1702 l 11748 1703 l 11727 1705 l
+ 11706 1708 l 11683 1711 l 11660 1716 l 11636 1721 l 11612 1727 l
+ 11587 1733 l 11560 1740 l 11534 1747 l 11506 1754 l 11479 1761 l
+ 11450 1768 l 11422 1774 l 11393 1780 l 11364 1786 l 11334 1791 l
+ 11305 1795 l 11275 1798 l 11245 1800 l 11215 1801 l 11184 1801 l
+ 11153 1800 l 11128 1798 l 11104 1796 l 11078 1793 l 11052 1790 l
+ 11025 1785 l 10997 1781 l 10968 1776 l 10939 1770 l 10908 1764 l
+ 10877 1758 l 10844 1751 l 10811 1744 l 10778 1737 l 10743 1730 l
+ 10708 1722 l 10673 1715 l 10637 1708 l 10601 1701 l 10565 1695 l
+ 10530 1688 l 10494 1682 l 10458 1677 l 10422 1672 l 10387 1668 l
+ 10352 1664 l 10318 1661 l 10284 1658 l 10250 1657 l 10216 1656 l
+ 10183 1655 l 10150 1656 l 10118 1658 l 10087 1660 l 10055 1663 l
+ 10024 1666 l 9992 1671 l 9960 1676 l 9927 1682 l 9894 1688 l
+ 9861 1695 l 9827 1703 l 9792 1711 l 9757 1720 l 9721 1729 l
+ 9685 1738 l 9649 1748 l 9613 1757 l 9576 1767 l 9539 1778 l
+ 9502 1788 l 9465 1798 l 9429 1807 l 9392 1817 l 9356 1826 l
+ 9320 1835 l 9285 1844 l 9250 1852 l 9216 1860 l 9182 1867 l
+ 9148 1873 l 9115 1879 l 9082 1884 l 9050 1889 l 9018 1892 l
+ 8987 1895 l 8955 1898 l 8919 1899 l 8883 1900 l 8847 1899 l
+ 8811 1898 l 8774 1896 l 8737 1893 l 8699 1889 l 8661 1884 l
+ 8623 1878 l 8585 1872 l 8546 1865 l 8508 1857 l 8470 1849 l
+ 8432 1840 l 8395 1830 l 8358 1821 l 8322 1811 l 8287 1801 l
+ 8254 1790 l 8221 1780 l 8189 1770 l 8159 1760 l 8130 1750 l
+ 8102 1740 l 8076 1730 l 8051 1721 l 8028 1712 l 8006 1703 l
+ 7985 1695 l 7965 1688 l 7931 1674 l 7899 1662 l 7871 1650 l
+ 7844 1640 l 7820 1631 l 7798 1623 l 7778 1617 l 7760 1611 l
+ 7743 1607 l 7728 1603 l 7715 1601 l 7702 1600 l 7691 1600 l
+ 7680 1601 l 7669 1603 l 7658 1605 l 7648 1607 l 7638 1610 l
+ 7627 1613 l 7615 1617 l 7601 1621 l 7587 1626 l 7571 1632 l
+ 7554 1638 l 7536 1645 l 7517 1653 l 7496 1661 l 7474 1670 l
+ 7452 1679 l 7428 1689 l 7403 1699 l 7378 1709 l 7352 1720 l
+ 7325 1731 l 7297 1743 l 7268 1755 l 7247 1763 l 7226 1772 l
+ 7204 1781 l 7182 1790 l 7158 1800 l 7133 1810 l 7108 1820 l
+ 7081 1831 l 7053 1842 l 7025 1853 l 6996 1864 l 6966 1875 l
+ 6935 1886 l 6904 1898 l 6873 1909 l 6841 1921 l 6809 1932 l
+ 6776 1943 l 6744 1954 l 6712 1964 l 6680 1974 l 6649 1984 l
+ 6618 1994 l 6587 2003 l 6557 2011 l 6527 2019 l 6498 2027 l
+ 6469 2034 l 6441 2041 l cp gs col34 1.00 shd ef gr gs col34 s gr 
+% Polyline
+n 675 6525 m 5850 6525 l 5850 6075 l 5625 6075 l 5625 5625 l 900 5625 l
+ 900 6075 l 675 6075 l cp gs col7 1.00 shd ef gr gs col7 s gr 
+% Polyline
+n 1125 6525 m 5355 6525 l 5400 5175 l 5175 5175 l 5175 4725 l 4950 4725 l
+ 4950 4275 l 1575 4275 l 1575 4725 l 1350 4725 l 1350 5175 l
+ 1125 5175 l cp gs col34 1.00 shd ef gr gs col34 s gr 
+% Polyline
+75.000 slw
+n 9450 4500 m 12465 2205 l gs col7 s gr 
+% Polyline
+n 9450 4500 m 9450 7785 l gs col7 s gr 
+% Polyline
+n 9450 4500 m 6075 1935 l gs col7 s gr 
+% Polyline
+n 12510 6435 m 9450 6435 l gs col7 s gr 
+% Polyline
+7.500 slw
+n 1800 6525 m 4725 6525 l 4725 3825 l 4500 3825 l 4500 3375 l 4275 3375 l
+ 4275 2925 l 4050 2925 l 4050 2475 l 2475 2475 l 2475 2925 l
+ 2250 2925 l 2250 3375 l 2025 3375 l 2025 3825 l 1800 3825 l
+ cp gs col35 1.00 shd ef gr gs col35 s gr 
+% Polyline
+n 2700 6525 m 3825 6525 l 3825 2025 l 3600 2025 l 3600 1575 l 2925 1575 l
+ 2925 2025 l 2700 2025 l cp gs col33 1.00 shd ef gr gs col33 s gr 
+% Polyline
+gs  clippath
+12068 6810 m 11970 6885 l 12022 6773 l 11937 6878 l 11984 6915 l cp
+clip
+n 12375 4455 m 12510 4635 l 12510 6210 l 11970 6885 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 12068 6810 m 11970 6885 l 12022 6773 l 12045 6791 l 12068 6810 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+gs  clippath
+7113 6004 m 7155 6120 l 7063 6037 l 7138 6149 l 7188 6116 l cp
+clip
+n 6705 5445 m 7155 6120 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 7113 6004 m 7155 6120 l 7063 6037 l 7088 6020 l 7113 6004 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+gs  clippath
+7304 4656 m 7200 4590 l 7323 4599 l 7195 4557 l 7176 4614 l cp
+clip
+n 7875 4815 m 7200 4590 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 7304 4656 m 7200 4590 l 7323 4599 l 7314 4628 l 7304 4656 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+gs  clippath
+11405 4128 m 11475 4230 l 11365 4173 l 11466 4262 l 11506 4217 l cp
+clip
+n 9585 2565 m 11475 4230 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 11405 4128 m 11475 4230 l 11365 4173 l 11385 4151 l 11405 4128 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+gs  clippath
+11712 4556 m 11835 4545 l 11732 4613 l 11859 4568 l 11839 4512 l cp
+clip
+n 10170 5130 m 11835 4545 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 11712 4556 m 11835 4545 l 11732 4613 l 11722 4585 l 11712 4556 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+gs  clippath
+9732 5411 m 9855 5400 l 9752 5468 l 9879 5423 l 9859 5367 l cp
+clip
+n 7920 6075 m 9855 5400 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 9732 5411 m 9855 5400 l 9752 5468 l 9742 5440 l 9732 5411 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+gs  clippath
+10823 5573 m 10935 5625 l 10812 5632 l 10944 5657 l 10955 5598 l cp
+clip
+n 9990 5445 m 10935 5625 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 10823 5573 m 10935 5625 l 10812 5632 l 10817 5603 l 10823 5573 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+gs  clippath
+10815 5280 m 10935 5310 l 10815 5340 l 10950 5340 l 10950 5280 l cp
+clip
+n 10215 5310 m 10935 5310 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 10815 5280 m 10935 5310 l 10815 5340 l 10815 5310 l 10815 5280 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+gs  clippath
+11955 4965 m 11925 5085 l 11895 4965 l 11895 5100 l 11955 5100 l cp
+clip
+n 11925 4590 m 11925 5085 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 11955 4965 m 11925 5085 l 11895 4965 l 11925 4965 l 11955 4965 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+gs  clippath
+9840 6720 m 9810 6840 l 9780 6720 l 9780 6855 l 9840 6855 l cp
+clip
+n 9810 5490 m 9810 6840 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 9840 6720 m 9810 6840 l 9780 6720 l 9810 6720 l 9840 6720 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+gs  clippath
+10847 5943 m 10935 6030 l 10816 5995 l 10933 6063 l 10963 6012 l cp
+clip
+n 9945 5445 m 10935 6030 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 10847 5943 m 10935 6030 l 10816 5995 l 10832 5969 l 10847 5943 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+gs  clippath
+10698 2634 m 10800 2565 l 10742 2674 l 10832 2574 l 10788 2534 l cp
+clip
+n 8865 4725 m 10800 2565 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 10698 2634 m 10800 2565 l 10742 2674 l 10720 2654 l 10698 2634 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+30.000 slw
+n 675 6075 m 5850 6075 l gs col34 1.00 shd ef gr gs col0 s gr 
+% Polyline
+7.500 slw
+ [15 15] 15 sd
+gs  clippath
+645 6195 m 675 6075 l 705 6195 l 705 6060 l 645 6060 l cp
+clip
+n 675 6525 m 675 6075 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 645 6195 m 675 6075 l 705 6195 l 675 6195 l 645 6195 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+ [15 15] 15 sd
+gs  clippath
+5880 6405 m 5850 6525 l 5820 6405 l 5820 6540 l 5880 6540 l cp
+clip
+n 5850 6075 m 5850 6525 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 5880 6405 m 5850 6525 l 5820 6405 l 5850 6405 l 5880 6405 l  cp gs col7 1.00 shd ef gr  col0 s
+% Polyline
+30.000 slw
+n 900 5625 m 5625 5625 l gs col34 1.00 shd ef gr gs col0 s gr 
+% Polyline
+n 1125 5175 m 5400 5175 l gs col34 1.00 shd ef gr gs col0 s gr 
+% Polyline
+n 1350 4725 m 5175 4725 l gs col34 1.00 shd ef gr gs col0 s gr 
+% Polyline
+n 1575 4275 m 4950 4275 l gs col34 1.00 shd ef gr gs col0 s gr 
+% Polyline
+n 1800 3825 m 4725 3825 l gs col34 1.00 shd ef gr gs col0 s gr 
+% Polyline
+n 2025 3375 m 4500 3375 l gs col34 1.00 shd ef gr gs col0 s gr 
+% Polyline
+n 2250 2925 m 4275 2925 l gs col34 1.00 shd ef gr gs col0 s gr 
+% Polyline
+n 2475 2475 m 4050 2475 l gs col34 1.00 shd ef gr gs col0 s gr 
+% Polyline
+n 2700 2025 m 3825 2025 l gs col34 1.00 shd ef gr gs col0 s gr 
+% Polyline
+n 2925 1575 m 3600 1575 l gs col34 1.00 shd ef gr gs col0 s gr 
+% Polyline
+7.500 slw
+ [15 15] 15 sd
+gs  clippath
+870 5745 m 900 5625 l 930 5745 l 930 5610 l 870 5610 l cp
+clip
+n 900 6075 m 900 5625 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 870 5745 m 900 5625 l 930 5745 l 900 5745 l 870 5745 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+ [15 15] 15 sd
+gs  clippath
+1095 5295 m 1125 5175 l 1155 5295 l 1155 5160 l 1095 5160 l cp
+clip
+n 1125 6525 m 1125 5175 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 1095 5295 m 1125 5175 l 1155 5295 l 1125 5295 l 1095 5295 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+ [15 15] 15 sd
+gs  clippath
+1320 4845 m 1350 4725 l 1380 4845 l 1380 4710 l 1320 4710 l cp
+clip
+n 1350 5175 m 1350 4725 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 1320 4845 m 1350 4725 l 1380 4845 l 1350 4845 l 1320 4845 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+ [15 15] 15 sd
+gs  clippath
+1545 4395 m 1575 4275 l 1605 4395 l 1605 4260 l 1545 4260 l cp
+clip
+n 1575 4725 m 1575 4275 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 1545 4395 m 1575 4275 l 1605 4395 l 1575 4395 l 1545 4395 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+ [15 15] 15 sd
+gs  clippath
+1770 3945 m 1800 3825 l 1830 3945 l 1830 3810 l 1770 3810 l cp
+clip
+n 1800 6525 m 1800 3825 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 1770 3945 m 1800 3825 l 1830 3945 l 1800 3945 l 1770 3945 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+ [15 15] 15 sd
+gs  clippath
+1995 3495 m 2025 3375 l 2055 3495 l 2055 3360 l 1995 3360 l cp
+clip
+n 2025 3825 m 2025 3375 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 1995 3495 m 2025 3375 l 2055 3495 l 2025 3495 l 1995 3495 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+ [15 15] 15 sd
+gs  clippath
+2220 3045 m 2250 2925 l 2280 3045 l 2280 2910 l 2220 2910 l cp
+clip
+n 2250 3375 m 2250 2925 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 2220 3045 m 2250 2925 l 2280 3045 l 2250 3045 l 2220 3045 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+ [15 15] 15 sd
+gs  clippath
+2445 2595 m 2475 2475 l 2505 2595 l 2505 2460 l 2445 2460 l cp
+clip
+n 2475 2925 m 2475 2475 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 2445 2595 m 2475 2475 l 2505 2595 l 2475 2595 l 2445 2595 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+ [15 15] 15 sd
+gs  clippath
+5655 5955 m 5625 6075 l 5595 5955 l 5595 6090 l 5655 6090 l cp
+clip
+n 5625 5625 m 5625 6075 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 5655 5955 m 5625 6075 l 5595 5955 l 5625 5955 l 5655 5955 l  cp gs col7 1.00 shd ef gr  col0 s
+% Polyline
+ [15 15] 15 sd
+gs  clippath
+5430 6405 m 5400 6525 l 5370 6405 l 5370 6540 l 5430 6540 l cp
+clip
+n 5400 5175 m 5400 6525 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 5430 6405 m 5400 6525 l 5370 6405 l 5400 6405 l 5430 6405 l  cp gs col7 1.00 shd ef gr  col0 s
+% Polyline
+ [15 15] 15 sd
+gs  clippath
+5205 5055 m 5175 5175 l 5145 5055 l 5145 5190 l 5205 5190 l cp
+clip
+n 5175 4725 m 5175 5175 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 5205 5055 m 5175 5175 l 5145 5055 l 5175 5055 l 5205 5055 l  cp gs col7 1.00 shd ef gr  col0 s
+% Polyline
+ [15 15] 15 sd
+gs  clippath
+4980 4605 m 4950 4725 l 4920 4605 l 4920 4740 l 4980 4740 l cp
+clip
+n 4950 4275 m 4950 4725 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 4980 4605 m 4950 4725 l 4920 4605 l 4950 4605 l 4980 4605 l  cp gs col7 1.00 shd ef gr  col0 s
+% Polyline
+ [15 15] 15 sd
+gs  clippath
+4755 6405 m 4725 6525 l 4695 6405 l 4695 6540 l 4755 6540 l cp
+clip
+n 4725 3825 m 4725 6525 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 4755 6405 m 4725 6525 l 4695 6405 l 4725 6405 l 4755 6405 l  cp gs col7 1.00 shd ef gr  col0 s
+% Polyline
+ [15 15] 15 sd
+gs  clippath
+4530 3705 m 4500 3825 l 4470 3705 l 4470 3840 l 4530 3840 l cp
+clip
+n 4500 3375 m 4500 3825 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 4530 3705 m 4500 3825 l 4470 3705 l 4500 3705 l 4530 3705 l  cp gs col7 1.00 shd ef gr  col0 s
+% Polyline
+ [15 15] 15 sd
+gs  clippath
+4305 3255 m 4275 3375 l 4245 3255 l 4245 3390 l 4305 3390 l cp
+clip
+n 4275 2925 m 4275 3375 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 4305 3255 m 4275 3375 l 4245 3255 l 4275 3255 l 4305 3255 l  cp gs col7 1.00 shd ef gr  col0 s
+% Polyline
+ [15 15] 15 sd
+gs  clippath
+4080 2805 m 4050 2925 l 4020 2805 l 4020 2940 l 4080 2940 l cp
+clip
+n 4050 2475 m 4050 2925 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 4080 2805 m 4050 2925 l 4020 2805 l 4050 2805 l 4080 2805 l  cp gs col7 1.00 shd ef gr  col0 s
+% Polyline
+ [15 15] 15 sd
+gs  clippath
+2670 2145 m 2700 2025 l 2730 2145 l 2730 2010 l 2670 2010 l cp
+clip
+n 2700 6525 m 2700 2025 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 2670 2145 m 2700 2025 l 2730 2145 l 2700 2145 l 2670 2145 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+ [15 15] 15 sd
+gs  clippath
+3855 6405 m 3825 6525 l 3795 6405 l 3795 6540 l 3855 6540 l cp
+clip
+n 3825 2025 m 3825 6525 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 3855 6405 m 3825 6525 l 3795 6405 l 3825 6405 l 3855 6405 l  cp gs col7 1.00 shd ef gr  col0 s
+% Polyline
+ [15 15] 15 sd
+gs  clippath
+3630 1905 m 3600 2025 l 3570 1905 l 3570 2040 l 3630 2040 l cp
+clip
+n 3600 1575 m 3600 2025 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 3630 1905 m 3600 2025 l 3570 1905 l 3600 1905 l 3630 1905 l  cp gs col7 1.00 shd ef gr  col0 s
+% Polyline
+ [15 15] 15 sd
+gs  clippath
+2895 1695 m 2925 1575 l 2955 1695 l 2955 1560 l 2895 1560 l cp
+clip
+n 2925 2025 m 2925 1575 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 2895 1695 m 2925 1575 l 2955 1695 l 2925 1695 l 2895 1695 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+45.000 slw
+gs  clippath
+6087 6495 m 6207 6525 l 6087 6555 l 6360 6555 l 6360 6495 l cp
+clip
+n 540 6525 m 6300 6525 l gs 0.00 setgray ef gr gs col0 s gr gr
+
+% arrowhead
+n 6087 6495 m 6207 6525 l 6087 6555 l 6087 6525 l 6087 6495 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+7.500 slw
+gs  clippath
+3681 6720 m 3825 6750 l 3681 6780 l 3840 6780 l 3840 6720 l cp
+2844 6780 m 2700 6750 l 2844 6720 l 2685 6720 l 2685 6780 l cp
+clip
+n 2700 6750 m 3825 6750 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 2844 6780 m 2700 6750 l 2844 6720 l 2820 6750 l 2844 6780 l  cp gs col7 1.00 shd ef gr  col0 s
+% arrowhead
+n 3681 6720 m 3825 6750 l 3681 6780 l 3705 6750 l 3681 6720 l  cp gs col7 1.00 shd ef gr  col0 s
+% Polyline
+gs  clippath
+5256 7170 m 5400 7200 l 5256 7230 l 5415 7230 l 5415 7170 l cp
+1269 7230 m 1125 7200 l 1269 7170 l 1110 7170 l 1110 7230 l cp
+clip
+n 1125 7200 m 5400 7200 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 1269 7230 m 1125 7200 l 1269 7170 l 1245 7200 l 1269 7230 l  cp gs col7 1.00 shd ef gr  col0 s
+% arrowhead
+n 5256 7170 m 5400 7200 l 5256 7230 l 5280 7200 l 5256 7170 l  cp gs col7 1.00 shd ef gr  col0 s
+% Polyline
+gs  clippath
+4581 6945 m 4725 6975 l 4581 7005 l 4740 7005 l 4740 6945 l cp
+1944 7005 m 1800 6975 l 1944 6945 l 1785 6945 l 1785 7005 l cp
+clip
+n 1800 6975 m 4725 6975 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 1944 7005 m 1800 6975 l 1944 6945 l 1920 6975 l 1944 7005 l  cp gs col7 1.00 shd ef gr  col0 s
+% arrowhead
+n 4581 6945 m 4725 6975 l 4581 7005 l 4605 6975 l 4581 6945 l  cp gs col7 1.00 shd ef gr  col0 s
+% Polyline
+gs  clippath
+5706 7395 m 5850 7425 l 5706 7455 l 5865 7455 l 5865 7395 l cp
+819 7455 m 675 7425 l 819 7395 l 660 7395 l 660 7455 l cp
+clip
+n 675 7425 m 5850 7425 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 819 7455 m 675 7425 l 819 7395 l 795 7425 l 819 7455 l  cp gs col7 1.00 shd ef gr  col0 s
+% arrowhead
+n 5706 7395 m 5850 7425 l 5706 7455 l 5730 7425 l 5706 7395 l  cp gs col7 1.00 shd ef gr  col0 s
+% Polyline
+1 slc
+ [15 45] 45 sd
+n 675 6570 m 675 7650 l gs col34 1.00 shd ef gr gs col0 s gr  [] 0 sd
+% Polyline
+ [15 45] 45 sd
+n 1125 6570 m 1125 7650 l gs col34 1.00 shd ef gr gs col0 s gr  [] 0 sd
+% Polyline
+ [15 45] 45 sd
+n 1800 6570 m 1800 7650 l gs col34 1.00 shd ef gr gs col0 s gr  [] 0 sd
+% Polyline
+ [15 45] 45 sd
+n 2700 6570 m 2700 7650 l gs col34 1.00 shd ef gr gs col0 s gr  [] 0 sd
+% Polyline
+ [15 45] 45 sd
+n 3825 6570 m 3825 7650 l gs col34 1.00 shd ef gr gs col0 s gr  [] 0 sd
+% Polyline
+ [15 45] 45 sd
+n 4725 6570 m 4725 7650 l gs col34 1.00 shd ef gr gs col0 s gr  [] 0 sd
+% Polyline
+ [15 45] 45 sd
+n 5400 6570 m 5400 7650 l gs col34 1.00 shd ef gr gs col0 s gr  [] 0 sd
+% Polyline
+ [15 45] 45 sd
+n 5850 6570 m 5850 7650 l gs col34 1.00 shd ef gr gs col0 s gr  [] 0 sd
+% Polyline
+0 slc
+n 750 225 m 450 225 450 1050 300 arcto 4 {pop} repeat
+  450 1350 12300 1350 300 arcto 4 {pop} repeat
+  12600 1350 12600 525 300 arcto 4 {pop} repeat
+  12600 225 750 225 300 arcto 4 {pop} repeat
+ cp gs col34 1.00 shd ef gr gs col0 s gr 
+% Polyline
+n 8835 2250 m 8775 2250 8775 2415 60 arcto 4 {pop} repeat
+  8775 2475 10110 2475 60 arcto 4 {pop} repeat
+  10170 2475 10170 2310 60 arcto 4 {pop} repeat
+  10170 2250 8835 2250 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr 
+% Polyline
+n 10635 2250 m 10575 2250 10575 2415 60 arcto 4 {pop} repeat
+  10575 2475 11865 2475 60 arcto 4 {pop} repeat
+  11925 2475 11925 2310 60 arcto 4 {pop} repeat
+  11925 2250 10635 2250 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr 
+% Polyline
+n 11490 4275 m 11430 4275 11430 4440 60 arcto 4 {pop} repeat
+  11430 4500 12315 4500 60 arcto 4 {pop} repeat
+  12375 4500 12375 4335 60 arcto 4 {pop} repeat
+  12375 4275 11490 4275 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr 
+% Polyline
+n 11040 5175 m 10980 5175 10980 5340 60 arcto 4 {pop} repeat
+  10980 5400 12315 5400 60 arcto 4 {pop} repeat
+  12375 5400 12375 5235 60 arcto 4 {pop} repeat
+  12375 5175 11040 5175 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr 
+% Polyline
+n 9735 5175 m 9675 5175 9675 5340 60 arcto 4 {pop} repeat
+  9675 5400 10110 5400 60 arcto 4 {pop} repeat
+  10170 5400 10170 5235 60 arcto 4 {pop} repeat
+  10170 5175 9735 5175 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr 
+% Polyline
+n 7260 6075 m 7200 6075 7200 6240 60 arcto 4 {pop} repeat
+  7200 6300 7815 6300 60 arcto 4 {pop} repeat
+  7875 6300 7875 6135 60 arcto 4 {pop} repeat
+  7875 6075 7260 6075 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr 
+% Polyline
+n 6810 2250 m 6750 2250 6750 2415 60 arcto 4 {pop} repeat
+  6750 2475 8130 2475 60 arcto 4 {pop} repeat
+  8190 2475 8190 2310 60 arcto 4 {pop} repeat
+  8190 2250 6810 2250 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr 
+% Polyline
+n 6360 3375 m 6300 3375 6300 3540 60 arcto 4 {pop} repeat
+  6300 3600 7545 3600 60 arcto 4 {pop} repeat
+  7605 3600 7605 3435 60 arcto 4 {pop} repeat
+  7605 3375 6360 3375 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr 
+% Polyline
+n 6360 4275 m 6300 4275 6300 4440 60 arcto 4 {pop} repeat
+  6300 4500 7275 4500 60 arcto 4 {pop} repeat
+  7335 4500 7335 4335 60 arcto 4 {pop} repeat
+  7335 4275 6360 4275 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr 
+% Polyline
+n 6360 5175 m 6300 5175 6300 5340 60 arcto 4 {pop} repeat
+  6300 5400 7140 5400 60 arcto 4 {pop} repeat
+  7200 5400 7200 5235 60 arcto 4 {pop} repeat
+  7200 5175 6360 5175 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr 
+% Polyline
+gs  clippath
+7365 5340 m 7245 5310 l 7365 5280 l 7230 5280 l 7230 5340 l cp
+clip
+n 9630 5310 m 7245 5310 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 7365 5340 m 7245 5310 l 7365 5280 l 7365 5310 l 7365 5340 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+gs  clippath
+7500 4395 m 7380 4365 l 7500 4335 l 7365 4335 l 7365 4395 l cp
+clip
+n 11385 4365 m 7380 4365 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 7500 4395 m 7380 4365 l 7500 4335 l 7500 4365 l 7500 4395 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+n 11040 5580 m 10980 5580 10980 5745 60 arcto 4 {pop} repeat
+  10980 5805 12180 5805 60 arcto 4 {pop} repeat
+  12240 5805 12240 5640 60 arcto 4 {pop} repeat
+  12240 5580 11040 5580 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr 
+% Polyline
+n 11040 5985 m 10980 5985 10980 6150 60 arcto 4 {pop} repeat
+  10980 6210 12315 6210 60 arcto 4 {pop} repeat
+  12375 6210 12375 6045 60 arcto 4 {pop} repeat
+  12375 5985 11040 5985 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr 
+% Polyline
+gs  clippath
+9958 5554 m 9900 5445 l 10003 5514 l 9912 5414 l 9868 5454 l cp
+clip
+n 11205 6885 m 9900 5445 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 9958 5554 m 9900 5445 l 10003 5514 l 9981 5534 l 9958 5554 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+n 10590 6930 m 10530 6930 10530 7095 60 arcto 4 {pop} repeat
+  10530 7155 12225 7155 60 arcto 4 {pop} repeat
+  12285 7155 12285 6990 60 arcto 4 {pop} repeat
+  12285 6930 10590 6930 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr 
+% Polyline
+n 9690 6930 m 9630 6930 9630 7095 60 arcto 4 {pop} repeat
+  9630 7155 10110 7155 60 arcto 4 {pop} repeat
+  10170 7155 10170 6990 60 arcto 4 {pop} repeat
+  10170 6930 9690 6930 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr 
+/Times-Roman-iso ff 120.00 scf sf
+900 7560 m
+gs 1 -1 sc (Startup, Runtime, Shutdown) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+6345 2970 m
+gs 1 -1 sc (ap_ctx_get\(...,) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+10800 2745 m
+gs 1 -1 sc (ap_get_module_config\(...) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+10800 2880 m
+gs 1 -1 sc (->per_dir_config,) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+10800 3015 m
+gs 1 -1 sc (&ssl_module\)) col0 sh gr
+% Polyline
+n 7980 4770 m 7920 4770 7920 4935 60 arcto 4 {pop} repeat
+  7920 4995 9075 4995 60 arcto 4 {pop} repeat
+  9135 4995 9135 4830 60 arcto 4 {pop} repeat
+  9135 4770 7980 4770 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr 
+% Polyline
+gs  clippath
+7340 2610 m 7425 2520 l 7393 2639 l 7459 2521 l 7406 2492 l cp
+clip
+n 6975 3330 m 7425 2520 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 7340 2610 m 7425 2520 l 7393 2639 l 7367 2625 l 7340 2610 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+gs  clippath
+9336 2569 m 9450 2520 l 9373 2616 l 9480 2535 l 9444 2487 l cp
+clip
+n 7200 4230 m 9450 2520 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 9336 2569 m 9450 2520 l 9373 2616 l 9354 2593 l 9336 2569 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+gs  clippath
+7321 5196 m 7200 5220 l 7296 5142 l 7174 5199 l 7199 5254 l cp
+clip
+n 7875 4905 m 7200 5220 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 7321 5196 m 7200 5220 l 7296 5142 l 7309 5169 l 7321 5196 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+gs  clippath
+6720 4665 m 6750 4545 l 6780 4665 l 6780 4530 l 6720 4530 l cp
+clip
+n 6750 5130 m 6750 4545 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 6720 4665 m 6750 4545 l 6780 4665 l 6750 4665 l 6720 4665 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+ [15 15] 15 sd
+gs  clippath
+9279 4984 m 9175 4918 l 9298 4927 l 9170 4885 l 9151 4942 l cp
+clip
+n 9850 5143 m 9175 4918 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 9279 4984 m 9175 4918 l 9298 4927 l 9289 4956 l 9279 4984 l  cp gs 0.00 setgray ef gr  col0 s
+/Helvetica-Narrow-iso ff 120.00 scf sf
+6210 4680 m
+gs 1 -1 sc (->server) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+8280 6120 m
+gs 1 -1 sc (ap_ctx_get\(...,"ssl"\)) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+7740 2700 m
+gs 1 -1 sc (ap_get_module_config\(...) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+7740 2835 m
+gs 1 -1 sc (->module_config,) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+7740 2970 m
+gs 1 -1 sc (&ssl_module\)) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+6345 3105 m
+gs 1 -1 sc ("ssl_module"\)) col0 sh gr
+/Times-Roman-iso ff 120.00 scf sf
+1350 7335 m
+gs 1 -1 sc (Configuration Time) col0 sh gr
+/Times-Roman-iso ff 120.00 scf sf
+2025 7110 m
+gs 1 -1 sc (Connection Duration) col0 sh gr
+/Times-Roman-iso ff 120.00 scf sf
+2835 6885 m
+gs 1 -1 sc (Request Duration) col0 sh gr
+/Helvetica-Bold-iso ff 300.00 scf sf
+6345 6795 m
+gs 1 -1 sc (t) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+7110 5985 m
+gs 1 -1 sc (->client) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+7065 5085 m
+gs 1 -1 sc (->connection) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+7065 4770 m
+gs 1 -1 sc (->server) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+8010 5445 m
+gs 1 -1 sc (SSL_get_app_data\(\)) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+10530 4050 m
+gs 1 -1 sc (->pSSLCtx) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+7875 4275 m
+gs 1 -1 sc (SSL_CTX_get_app_data\(\)) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+10305 5535 m
+gs 1 -1 sc (SSL_get_current_cipher\(\)) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+10440 5940 m
+gs 1 -1 sc (SSL_get_session\(\)) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+9540 7335 m
+gs 1 -1 sc (SSL_get_{r,w}bio\(\)) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+10125 4680 m
+gs 1 -1 sc (SSL_get_SSL_CTX\(\)) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+10350 5175 m
+gs 1 -1 sc (SSL_get_SSL_METHOD\(\)) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+11745 4770 m
+gs 1 -1 sc (->method) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+9945 6480 m
+gs 1 -1 sc (X509_STORE_CTX_get_app_data\(\)) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+10980 6705 m
+gs 1 -1 sc (SSL_CTX_get_cert_store\(\)) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+8280 5130 m
+gs 1 -1 sc (SSL_get_app_data2\(\)) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+3645 1620 m
+gs 1 -1 sc (SSLDirConfig) col0 sh gr
+/Helvetica-Bold-iso ff 300.00 scf sf
+10935 3645 m
+gs 1 -1 sc (OpenSSL) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+10935 3825 m
+gs 1 -1 sc ([SSL]) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+11025 5760 m
+gs 1 -1 sc (SSL_CIPHER) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+10980 6165 m
+gs 1 -1 sc (SSL_SESSION) col0 sh gr
+/Helvetica-Bold-iso ff 300.00 scf sf
+10710 7605 m
+gs 1 -1 sc (OpenSSL) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+10575 7110 m
+gs 1 -1 sc (X509_STORE_CTX) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+6795 2430 m
+gs 1 -1 sc (SSLModConfig) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+8865 2430 m
+gs 1 -1 sc (SSLSrvConfig) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+6345 3555 m
+gs 1 -1 sc (ap_global_ctx) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+6345 4455 m
+gs 1 -1 sc (server_rec) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+6345 5355 m
+gs 1 -1 sc (conn_rec) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+9720 5355 m
+gs 1 -1 sc (SSL) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+10665 2430 m
+gs 1 -1 sc (SSLDirConfig) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+7290 6255 m
+gs 1 -1 sc (BUFF) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+11025 5355 m
+gs 1 -1 sc (SSL_METHOD) col0 sh gr
+% Polyline
+15.000 slw
+n 750 225 m 450 225 450 8250 300 arcto 4 {pop} repeat
+  450 8550 12300 8550 300 arcto 4 {pop} repeat
+  12600 8550 12600 525 300 arcto 4 {pop} repeat
+  12600 225 750 225 300 arcto 4 {pop} repeat
+ cp gs col0 s gr 
+/Helvetica-Bold-iso ff 180.00 scf sf
+11475 4455 m
+gs 1 -1 sc (SSL_CTX) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+8010 4950 m
+gs 1 -1 sc (request_rec) col0 sh gr
+/Times-Roman-iso ff 180.00 scf sf
+10575 675 m
+gs 1 -1 sc (Ralf S. Engelschall) col0 sh gr
+/Helvetica-Bold-iso ff 300.00 scf sf
+4275 675 m
+gs 1 -1 sc (Apache+mod_ssl+OpenSSL) col0 sh gr
+/Times-Roman-iso ff 150.00 scf sf
+10575 855 m
+gs 1 -1 sc (rse@engelschall.com) col0 sh gr
+/Times-Roman-iso ff 150.00 scf sf
+10575 1035 m
+gs 1 -1 sc (www.engelschall.com) col0 sh gr
+/Times-Roman-iso ff 180.00 scf sf
+900 675 m
+gs 1 -1 sc (Version 1.3) col0 sh gr
+/Times-Roman-iso ff 180.00 scf sf
+900 855 m
+gs 1 -1 sc (12-Apr-1999) col0 sh gr
+/Helvetica-Bold-iso ff 360.00 scf sf
+3915 1080 m
+gs 1 -1 sc (Data Structure Overview) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+9720 7110 m
+gs 1 -1 sc (BIO) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+10710 7785 m
+gs 1 -1 sc ([Crypto]) col0 sh gr
+/Helvetica-Bold-iso ff 300.00 scf sf
+8730 3465 m
+gs 1 -1 sc (mod_ssl) col0 sh gr
+/Helvetica-Bold-iso ff 300.00 scf sf
+8145 6750 m
+gs 1 -1 sc (Apache) col0 sh gr
+/Helvetica-Bold-iso ff 300.00 scf sf
+9000 8100 m
+gs 1 -1 sc (Chaining) col0 sh gr
+/Helvetica-Bold-iso ff 300.00 scf sf
+2745 8100 m
+gs 1 -1 sc (Lifetime) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+810 6255 m
+gs 1 -1 sc (ap_global_ctx) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+990 5805 m
+gs 1 -1 sc (SSLModConfig) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+4050 4455 m
+gs 1 -1 sc (SSL_CTX) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+4455 5355 m
+gs 1 -1 sc (server_rec) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+3870 4905 m
+gs 1 -1 sc (SSLSrvConfig) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+1845 4005 m
+gs 1 -1 sc (BUFF) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+2070 3555 m
+gs 1 -1 sc (conn_rec) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+2295 3105 m
+gs 1 -1 sc (BIO) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+2565 2655 m
+gs 1 -1 sc (SSL) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+3915 2070 m
+gs 1 -1 sc (request_rec) col0 sh gr
+$F2psEnd
+rs
+showpage
diff --git a/modules/ssl/libssl.module b/modules/ssl/libssl.module
new file mode 100644 (file)
index 0000000..72eeabb
--- /dev/null
@@ -0,0 +1,497 @@
+##                      _             _ 
+##  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
+## | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
+## | | | | | | (_) | (_| |   \__ \__ \ |  www.modssl.org
+## |_| |_| |_|\___/ \__,_|___|___/___/_|  ftp.modssl.org
+##                      |_____|         
+##  libssl.module
+##  Apache 1.3 Configuration mechanism module stub
+##
+
+##
+##  ====================================================================
+##  Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+## 
+##  Redistribution and use in source and binary forms, with or without
+##  modification, are permitted provided that the following conditions
+##  are met:
+## 
+##  1. Redistributions of source code must retain the above copyright
+##     notice, this list of conditions and the following disclaimer. 
+## 
+##  2. Redistributions in binary form must reproduce the above copyright
+##     notice, this list of conditions and the following
+##     disclaimer in the documentation and/or other materials
+##     provided with the distribution.
+## 
+##  3. All advertising materials mentioning features or use of this
+##     software must display the following acknowledgment:
+##     "This product includes software developed by 
+##      Ralf S. Engelschall <rse@engelschall.com> for use in the
+##      mod_ssl project (http://www.modssl.org/)."
+## 
+##  4. The names "mod_ssl" must not be used to endorse or promote
+##     products derived from this software without prior written
+##     permission. For written permission, please contact
+##     rse@engelschall.com.
+## 
+##  5. Products derived from this software may not be called "mod_ssl"
+##     nor may "mod_ssl" appear in their names without prior
+##     written permission of Ralf S. Engelschall.
+## 
+##  6. Redistributions of any form whatsoever must retain the following
+##     acknowledgment:
+##     "This product includes software developed by 
+##      Ralf S. Engelschall <rse@engelschall.com> for use in the
+##      mod_ssl project (http://www.modssl.org/)."
+## 
+##  THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+##  EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+##  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+##  PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+##  HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+##  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+##  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+##  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+##  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+##  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+##  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+##  OF THE POSSIBILITY OF SUCH DAMAGE.
+##  ====================================================================
+##
+
+                     # ``What you are missing, I suppose, is that I'm not
+                     #   prepared to give equal rights to Ralf on the basis
+                     #   that he's spent a few hours doing what he thinks is
+                     #   better than what I've spent the last 4 years on,
+                     #   and so he isn't prepared to cooperate with me.''
+                     #             -- Ben Laurie, Apache-SSL author
+
+Name: ssl_module
+ConfigStart
+
+    #
+    #   interface to the src/Configure script   
+    #
+    my_dir="`echo ${modfile} | sed -e 's:/[^/]*$::'`"
+    my_version="$my_dir/libssl.version"
+    my_outfile="Makefile.config"
+    my_prefix="      +"
+    my_prefixe="       "
+    SSL_CFLAGS=''
+    SSL_LDFLAGS=''
+    SSL_LIBS=''
+
+    #   
+    #   find a reasonable Bourne Shell for sub-shell calls
+    #
+    SH=sh
+    if [ -f /bin/bash ]; then
+        SH=/bin/bash
+    elif [ -f /bin/sh5 ]; then
+        SH=/bin/sh5
+    elif [ -f /bin/sh ]; then
+        SH=/bin/sh
+    fi
+
+    #
+    #   determine mod_ssl author version
+    #
+    A_ID=`cat $my_version | sed -e 's; .*;;'`
+    A_NAME=`echo $A_ID | sed -e 's;/.*;;'`
+    A_VER=`echo $A_ID | sed -e 's;.*/;;'`
+    A_VER_STR=`echo $A_VER | sed -e 's;-.*;;'`
+    case $A_VER_STR in
+        *.*b* )
+            A_VER_HEX=`echo "$A_VER_STR" | sed -e 's/b.*//' | awk -F. '{ printf("%d%02d", $1, $2); }' &&
+                       echo "$A_VER_STR" | sed -e 's/.*b//' | awk '{ printf("0%02d", $1); }'`
+            ;;
+        *.*.* )
+            A_VER_HEX=`echo "$A_VER_STR" | awk -F. '{ printf("%d%02d1%02d", $1, $2, $3); }'`
+            ;;
+    esac
+    echo "$my_prefix SSL interface: $A_NAME/$A_VER_STR"
+    SSL_VERSION="-DMOD_SSL_VERSION=\\\"$A_VER_STR\\\""
+  
+    #
+    #   determine optional mod_ssl product version
+    #
+    if [ ".`egrep '.*/.* .*/.*' $my_version`" != . ]; then
+        P_ID=`cat $my_version | sed -e 's;.* ;;'`
+        P_NAME=`echo $P_ID | sed -e 's;/.*;;'`
+        P_VER=`echo $P_ID | sed -e 's;.*/;;'`
+        P_VER_STR=`echo $P_VER | sed -e 's;-.*;;'`
+        case $P_VER_STR in
+            *.*b* )
+                P_VER_HEX=`echo "$P_VER_STR" | sed -e 's/b.*//' | awk -F. '{ printf("%d%02d", $1, $2); }' &&
+                           echo "$P_VER_STR" | sed -e 's/.*b//' | awk '{ printf("0%02d", $1); }'`
+                ;;
+            *.*.* )
+                P_VER_HEX=`echo "$P_VER_STR" | awk -F. '{ printf("%d%02d1%02d", $1, $2, $3); }'`
+                ;;
+        esac
+        echo "$my_prefix SSL product: $P_NAME/$P_VER_STR"
+        SSL_VERSION="$SSL_VERSION -DSSL_PRODUCT_NAME=\\\"$P_NAME\\\""
+        SSL_VERSION="$SSL_VERSION -DSSL_PRODUCT_VERSION=\\\"$P_VER_STR\\\""
+    fi
+
+    #
+    #   determine object build type
+    #
+    case $modfile in
+        *.so ) my_buildtype="DSO" ;;
+        *    ) my_buildtype="OBJ" ;;
+    esac
+    echo "$my_prefix SSL interface build type: $my_buildtype"
+
+    #   
+    #   determine SSL rules
+    #
+    if [ ".$APXS_MODE" = .YES ]; then
+        my_rule_SSL_COMPAT=$SSL_COMPAT
+        my_rule_SSL_SDBM=$SSL_SDBM
+        my_rule_SSL_EXPERIMENTAL=$SSL_EXPERIMENTAL
+        my_rule_SSL_CONSERVATIVE=$SSL_CONSERVATIVE
+        my_rule_SSL_VENDOR=$SSL_VENDOR
+    else
+        my_rule_SSL_COMPAT=`$SH helpers/CutRule SSL_COMPAT $file`
+        my_rule_SSL_SDBM=`$SH helpers/CutRule SSL_SDBM $file`
+        my_rule_SSL_EXPERIMENTAL=`$SH helpers/CutRule SSL_EXPERIMENTAL $file`
+        my_rule_SSL_CONSERVATIVE=`$SH helpers/CutRule SSL_CONSERVATIVE $file`
+        my_rule_SSL_VENDOR=`$SH helpers/CutRule SSL_VENDOR $file`
+    fi
+
+    #
+    #   determine compatibility mode
+    #
+    if [ ".$my_rule_SSL_COMPAT" = .yes ]; then
+        echo "$my_prefix SSL interface compatibility: enabled"
+        SSL_CFLAGS="$SSL_CFLAGS -DSSL_COMPAT"
+    else
+        echo "$my_prefix SSL interface compatibility: disabled"
+    fi
+
+    #
+    #   determine experimental mode
+    #
+    if [ ".$my_rule_SSL_EXPERIMENTAL" = .yes ]; then
+        echo "$my_prefix SSL interface experimental code: enabled"
+        SSL_CFLAGS="$SSL_CFLAGS -DSSL_EXPERIMENTAL"
+    else
+        echo "$my_prefix SSL interface experimental code: disabled"
+    fi
+
+    #
+    #   determine conservative mode
+    #
+    if [ ".$my_rule_SSL_CONSERVATIVE" = .yes ]; then
+        echo "$my_prefix SSL interface conservative code: enabled"
+        SSL_CFLAGS="$SSL_CFLAGS -DSSL_CONSERVATIVE"
+    else
+        echo "$my_prefix SSL interface conservative code: disabled"
+    fi
+
+    # 
+    #   determine vendor mode
+    #
+    SSL_VENDOR_OBJS=''
+    SSL_VENDOR_OBJS_PIC=''
+    if [ ".$my_rule_SSL_VENDOR" = .yes ]; then
+        echo "$my_prefix SSL interface vendor extensions: enabled"
+        SSL_CFLAGS="$SSL_CFLAGS -DSSL_VENDOR"
+        my_src="`cd $my_dir && echo ssl_vendor*.c`"
+        if [ ".$my_src" != . -a ".$my_src" != ".ssl_vendor*.c" ]; then
+            SSL_CFLAGS="$SSL_CFLAGS -DSSL_VENDOR_OBJS"
+            SSL_VENDOR_OBJS="`echo $my_src | sed -e 's;\.c;.o;g'`"
+            SSL_VENDOR_OBJS_PIC="`echo $my_src | sed -e 's;\.c;.lo;g'`"
+            echo "$my_prefix SSL interface vendor objects: $SSL_VENDOR_OBJS"
+        fi
+    else
+        echo "$my_prefix SSL interface vendor extensions: disabled"
+    fi
+
+    #
+    #   determine DBM support library
+    #   (src/Configure has DBM_LIB predefined for some platforms)
+    #
+    if [ ".$APXS_MODE" != .YES ]; then
+        SSL_DBM_NAME=''
+        #   1. check for predefined DBM lib
+        if [ ".$DBM_LIB" != . ]; then
+            LIBS_ORIG="$LIBS"
+            LIBS="$LIBS $DBM_LIB"
+            if $SH helpers/TestCompile func dbm_open; then
+                SSL_DBM_NAME="Configured DBM ($DBM_LIB)"
+                SSL_DBM_FLAG="$DBM_LIB"
+            fi
+            LIBS="$LIBS_ORIG"
+        fi
+        #   2. check for various vendor DBM libs
+        if [ ".$SSL_DBM_NAME" = . ]; then
+            if $SH helpers/TestCompile func dbm_open; then
+                SSL_DBM_NAME='Vendor DBM (libc)'
+                SSL_DBM_FLAG=''
+            elif $SH helpers/TestCompile lib dbm dbm_open; then
+                SSL_DBM_NAME='Vendor DBM (libdbm)'
+                SSL_DBM_FLAG='-ldbm'
+            elif $SH helpers/TestCompile lib ndbm dbm_open; then
+                SSL_DBM_NAME='Vendor DBM (libndbm)'
+                SSL_DBM_FLAG='-lndbm'
+            fi
+        fi
+        #   3. let the SSL_SDBM rule override decisions
+        if [ ".$my_rule_SSL_SDBM" = .yes ]; then
+            # force us to fallback to SDBM
+            SSL_DBM_NAME='' 
+        fi
+        if [ ".$my_rule_SSL_SDBM" = .no ]; then
+            #   for us to never use SDBM,  but be
+            #   careful when no DBM was found at all
+            if [ ".$SSL_DBM_NAME" = . ]; then
+                echo "Error: SDBM is needed, because no custom or vendor DBM library available!" 1>&2  
+                echo "Hint:  Allow us to choose SDBM by changing the rule SSL_SDBM, please." 1>&2  
+                exit 1
+            fi
+        fi
+        #   4. override decision on a few brain-dead platforms
+        if [ ".$my_rule_SSL_SDBM" = .default ]; then
+            case "$OS" in
+                Linux )
+                    #   force Linux boxes to use builtin SDBM per default because 
+                    #   of too much broken vendor DBM libraries on this platform
+                    SSL_DBM_NAME=''
+                    ;;
+            esac
+        fi
+        #   5. finally configure the chosen DBM lib
+        if [ ".$SSL_DBM_NAME" != . ]; then
+            echo "$my_prefix SSL interface plugin: $SSL_DBM_NAME"
+            my_dbm_already_used=`echo $LIBS | grep -- " $SSL_DBM_FLAG"`
+            if [ ".$my_buildtype" = .OBJ -a ".$my_dbm_already_used" != . ]; then
+                :
+            else
+                SSL_LIBS="$SSL_LIBS $SSL_DBM_FLAG"
+            fi
+        else
+            echo "$my_prefix SSL interface plugin: Built-in SDBM"
+            SSL_CFLAGS="$SSL_CFLAGS -DSSL_USE_SDBM"
+        fi
+    fi
+
+    #
+    #   determine SSL_BASE
+    #
+    if [ ".$SSL_BASE" = . ]; then
+        SSL_BASE=`egrep '^SSL_BASE=' $file | tail -1 | awk -F= '{print $2}'`
+        if [ ".$SSL_BASE" = . ]; then
+            if [ -d /usr/local/ssl ]; then
+                SSL_BASE="/usr/local/ssl"
+            else
+                SSL_BASE="SYSTEM"
+            fi
+        fi
+    fi
+    case $SSL_BASE in
+        SYSTEM ) ;;
+        /* ) ;;
+         * ) SSL_BASE="`cd ../$SSL_BASE; pwd`" ;;
+    esac
+    if [ ".$SSL_BASE" = .SYSTEM ]; then
+        echo "$my_prefix SSL library path: [SYSTEM]"
+    else
+        if [ ! -d "$SSL_BASE" ]; then
+            echo "Error: Cannot find SSL installation in $SSL_BASE" 1>&2  
+            echo "Hint:  Please provide us with the location of OpenSSL" 1>&2
+            echo "       via the environment variable SSL_BASE." 1>&2
+            exit 1
+        fi
+        echo "$my_prefix SSL library path: $SSL_BASE"
+    fi
+
+    #
+    #   determine location of OpenSSL binaries
+    #   (we still search also for `ssleay' to allow us to
+    #   better complain about the actually installed version)
+    #
+    SSL_BINDIR=""
+    if [ ".$SSL_BASE" = .SYSTEM ]; then
+        for name in openssl ssleay; do
+            for p in . `echo $PATH | sed -e 's/:/ /g'`; do
+                if [ -f "$p/$name" ]; then
+                    SSL_PROGRAM="$p/$name"
+                    SSL_BINDIR="$p"
+                    break
+                fi
+            done
+            if [ ".$SSL_BINDIR" != . ]; then
+                break;
+            fi
+        done
+        if [ ".$SSL_BINDIR" = . ]; then
+            echo "Error: Cannot find SSL binaries in $PATH" 1>&2
+            exit 1
+        fi
+    else
+        for name in openssl ssleay; do
+            if [ -f "$SSL_BASE/bin/$name" ]; then
+                SSL_PROGRAM="$SSL_BASE/bin/$name"
+                SSL_BINDIR='$(SSL_BASE)/bin'
+                break;
+            fi
+            if [ -f "$SSL_BASE/apps/$name" ]; then
+                SSL_PROGRAM="$SSL_BASE/apps/$name"
+                SSL_BINDIR='$(SSL_BASE)/apps'
+                break;
+            fi
+        done
+        if [ ".$SSL_BINDIR" = . ]; then
+            echo "Error: Cannot find SSL binaries under $SSL_BASE" 1>&2
+            exit 1
+        fi
+    fi
+
+    #
+    #   SSL version
+    #
+    SSL_VERSION_ID="`$SSL_PROGRAM version`"
+    echo "$my_prefix SSL library version: $SSL_VERSION_ID"
+    case $SSL_VERSION_ID in
+        *0.[5678].*|*0.9.[012]* )
+            echo "Error: OpenSSL VERSIONS BELOW 0.9.3 ARE NO LONGER SUPPORTED."
+            echo "Hint:  Use OpenSSL version 0.9.3 or higher!"
+            exit 1
+            ;;
+    esac
+
+    #
+    #   SSL engine support
+    #
+    case $SSL_VERSION_ID in
+        *0.9.6*engine* | *0.9.6a*engine* | *0.9.[789]* )
+            SSL_CFLAGS="$SSL_CFLAGS -DSSL_ENGINE"
+            ;;
+    esac
+
+    #
+    #   determine location of OpenSSL headers
+    #
+    if [ ".$SSL_BASE" = .SYSTEM ]; then
+        SSL_INCDIR=""
+        for p in . /usr/include /usr/include/ssl/ /usr/local/include /usr/local/include/ssl; do
+            if [ -f "$p/openssl/ssl.h" ]; then
+                SSL_INCDIR="$p"
+                break
+            fi
+        done
+        if [ ".$SSL_INCDIR" = . ]; then
+            echo "Error: Cannot find SSL header files in any of the following dirs:" 1>&2
+            echo "Error: . /usr/include /usr/include/ssl/ /usr/local/include /usr/local/include/ssl" 1>&2
+            exit 1
+        fi
+    else
+        if [ -f "$SSL_BASE/include/openssl/ssl.h" ]; then
+            SSL_INCDIR='$(SSL_BASE)/include'
+        else
+            echo "Error: Cannot find SSL header files under $SSL_BASE" 1>&2
+            exit 1
+        fi
+    fi
+    if [ ".$SSL_INCDIR" != "./usr/include" ]; then
+        SSL_CFLAGS="$SSL_CFLAGS -I\$(SSL_INCDIR)"
+    fi
+
+    #
+    #  determine location of OpenSSL libraries
+    #
+    if [ ".$SSL_BASE" = .SYSTEM ]; then
+        SSL_LIBDIR=""
+        for p in . /lib /usr/lib /usr/local/lib; do
+            if [ -f "$p/libssl.a" -o -f "$p/libssl.so" ]; then
+                SSL_LIBDIR="$p"
+                my_real_ssl_libdir="$p"
+                break
+            fi
+        done
+        if [ ".$SSL_LIBDIR" = . ]; then
+            echo "Error: Cannot find SSL library files in any of the following dirs:" 1>&2
+            echo "Error: . /lib /usr/lib /usr/local/lib" 1>&2
+            exit 1
+        fi
+    else
+        if [ -f "$SSL_BASE/libssl.a" -o -f "$SSL_BASE/libssl.so" ]; then
+            SSL_LIBDIR='$(SSL_BASE)'
+            my_real_ssl_libdir="$SSL_BASE"
+        elif [ -f "$SSL_BASE/lib/libssl.a" -o -f "$SSL_BASE/lib/libssl.so" ]; then
+            SSL_LIBDIR='$(SSL_BASE)/lib'
+            my_real_ssl_libdir="$SSL_BASE/lib"
+        else
+            echo "Error: Cannot find SSL library files under $SSL_BASE" 1>&2
+            exit 1
+        fi
+    fi
+    SSL_LDFLAGS="$SSL_LDFLAGS -L\$(SSL_LIBDIR)"
+    SSL_LIBS="$SSL_LIBS -lssl -lcrypto"
+
+    #
+    #   SSL installation type
+    #
+    case $SSL_BINDIR in
+        */apps ) my_type="source tree only" ;;
+             * ) my_type="installed package" ;;
+    esac
+    case $SSL_BASE in
+        SYSTEM ) my_note="(system-wide)" ;;
+        *      ) my_note="(stand-alone)" ;;
+    esac
+    echo "$my_prefix SSL library type: $my_type $my_note"
+
+    #
+    #   Special GCC/DSO support
+    #
+    #   Under some platforms where GCC is used we have to link the DSO
+    #   (libssl.so) explicitly against the GCC library (libgcc) to avoid
+    #   problems with missing symbols like __umoddi3, etc.
+    #
+    #   Notice: When GCC is installed as "cc" we assume it's really
+    #           well incorporated into the system and no hack is
+    #           needed (like on FreeBSD, Linux, etc.)
+    #
+    if [ ".$my_buildtype" = .DSO ]; then
+        my_CC=`echo "$CC" | sed -e 's/ .*//'`
+        case $my_CC in
+            gcc|*/gcc|egcs|*/egcs|egcc|*/egcc|pgcc|*/pgcc )
+                gcclibdir="`$CC --print-libgcc-file-name | sed -e 's;/[^/]*$;;'`"
+                SSL_LIBS="$SSL_LIBS -L$gcclibdir -lgcc"
+                ;;
+        esac
+    fi
+
+    #
+    #   adjust the Apache build environment
+    #
+    echo "SSL_BASE=$SSL_BASE" >>$my_outfile
+    echo "SSL_BINDIR=$SSL_BINDIR" >>$my_outfile
+    echo "SSL_INCDIR=$SSL_INCDIR" >>$my_outfile
+    echo "SSL_LIBDIR=$SSL_LIBDIR" >>$my_outfile
+    echo "SSL_PROGRAM=$SSL_PROGRAM" >>$my_outfile
+    echo "SSL_VERSION=$SSL_VERSION" >>$my_outfile
+    echo "SSL_CFLAGS=$SSL_CFLAGS" >>$my_outfile
+    echo "SSL_VENDOR_OBJS=$SSL_VENDOR_OBJS" >>$my_outfile
+    echo "SSL_VENDOR_OBJS_PIC=$SSL_VENDOR_OBJS_PIC" >>$my_outfile
+    if [ ".$my_buildtype" = .DSO ]; then
+        #   under DSO we link ourself
+        echo "SSL_LIBS=$SSL_LIBS" >>$my_outfile
+        echo "SSL_LDFLAGS=$SSL_LDFLAGS" >>$my_outfile
+    else
+        #   else we are linked with httpd
+        LDFLAGS="$LDFLAGS $SSL_LDFLAGS"
+        LIBS="$LIBS $SSL_LIBS"
+    fi
+    CFLAGS="$CFLAGS -DMOD_SSL=$A_VER_HEX"
+    if [ ".$P_ID" != . ]; then
+        CFLAGS="$CFLAGS -DSSL_PRODUCT=$P_VER_HEX"
+    fi
+    RULE_EAPI=yes
+
+ConfigEnd
+
diff --git a/modules/ssl/libssl.version b/modules/ssl/libssl.version
new file mode 100644 (file)
index 0000000..a053095
--- /dev/null
@@ -0,0 +1 @@
+mod_ssl/2.8.3-1.3.19
diff --git a/modules/ssl/mod_ssl.c b/modules/ssl/mod_ssl.c
new file mode 100644 (file)
index 0000000..2d1b5e1
--- /dev/null
@@ -0,0 +1,248 @@
+/*                      _             _
+**  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
+** | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
+** | | | | | | (_) | (_| |   \__ \__ \ |  www.modssl.org
+** |_| |_| |_|\___/ \__,_|___|___/___/_|  ftp.modssl.org
+**                      |_____|
+**  mod_ssl.c
+**  Apache API interface structures
+*/
+
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ *    nor may "mod_ssl" appear in their names without prior
+ *    written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+                             /* ``I'll be surprised if
+                                  others think that what you
+                                  are doing is honourable.''
+                                    -- Ben Laurie, Apache-SSL author */
+#include "mod_ssl.h"
+
+/*  _________________________________________________________________
+**
+**  Apache API glue structures
+**  _________________________________________________________________
+*/
+
+/*
+ *  identify the module to SCCS `what' and RCS `ident' commands
+ */
+static char const sccsid[] = "@(#) mod_ssl/" MOD_SSL_VERSION " >";
+static char const rcsid[]  = "$Id: mod_ssl.c,v 1.1 2001/05/04 21:54:42 rse Exp $";
+
+/*
+ *  the table of configuration directives we provide
+ */
+static command_rec ssl_config_cmds[] = {
+    /*
+     * Global (main-server) context configuration directives
+     */
+    AP_SRV_CMD(Mutex, TAKE1,
+               "SSL lock for handling internal mutual exclusions "
+               "(`none', `file:/path/to/file')")
+    AP_SRV_CMD(PassPhraseDialog, TAKE1,
+               "SSL dialog mechanism for the pass phrase query "
+               "(`builtin', `exec:/path/to/program')")
+    AP_SRV_CMD(SessionCache, TAKE1,
+               "SSL Session Cache storage "
+               "(`none', `dbm:/path/to/file')")
+#ifdef SSL_EXPERIMENTAL_ENGINE
+    AP_SRV_CMD(CryptoDevice, TAKE1,
+               "SSL external Crypto Device usage "
+               "(`builtin', `...')")
+#endif
+    AP_SRV_CMD(RandomSeed, TAKE23,
+               "SSL Pseudo Random Number Generator (PRNG) seeding source "
+               "(`startup|connect builtin|file:/path|exec:/path [bytes]')")
+
+    /*
+     * Per-server context configuration directives
+     */
+    AP_SRV_CMD(Engine, FLAG,
+               "SSL switch for the protocol engine "
+               "(`on', `off')")
+    AP_ALL_CMD(CipherSuite, TAKE1,
+               "Colon-delimited list of permitted SSL Ciphers "
+               "(`XXX:...:XXX' - see manual)")
+    AP_SRV_CMD(CertificateFile, TAKE1,
+               "SSL Server Certificate file "
+               "(`/path/to/file' - PEM or DER encoded)")
+    AP_SRV_CMD(CertificateKeyFile, TAKE1,
+               "SSL Server Private Key file "
+               "(`/path/to/file' - PEM or DER encoded)")
+    AP_SRV_CMD(CertificateChainFile, TAKE1,
+               "SSL Server CA Certificate Chain file "
+               "(`/path/to/file' - PEM encoded)")
+#ifdef SSL_EXPERIMENTAL_PERDIRCA
+    AP_ALL_CMD(CACertificatePath, TAKE1,
+               "SSL CA Certificate path "
+               "(`/path/to/dir' - contains PEM encoded files)")
+    AP_ALL_CMD(CACertificateFile, TAKE1,
+               "SSL CA Certificate file "
+               "(`/path/to/file' - PEM encoded)")
+#else
+    AP_SRV_CMD(CACertificatePath, TAKE1,
+               "SSL CA Certificate path "
+               "(`/path/to/dir' - contains PEM encoded files)")
+    AP_SRV_CMD(CACertificateFile, TAKE1,
+               "SSL CA Certificate file "
+               "(`/path/to/file' - PEM encoded)")
+#endif
+    AP_SRV_CMD(CARevocationPath, TAKE1,
+               "SSL CA Certificate Revocation List (CRL) path "
+               "(`/path/to/dir' - contains PEM encoded files)")
+    AP_SRV_CMD(CARevocationFile, TAKE1,
+               "SSL CA Certificate Revocation List (CRL) file "
+               "(`/path/to/file' - PEM encoded)")
+    AP_ALL_CMD(VerifyClient, TAKE1,
+               "SSL Client verify type "
+               "(`none', `optional', `require', `optional_no_ca')")
+    AP_ALL_CMD(VerifyDepth, TAKE1,
+               "SSL Client verify depth "
+               "(`N' - number of intermediate certificates)")
+    AP_SRV_CMD(SessionCacheTimeout, TAKE1,
+               "SSL Session Cache object lifetime "
+               "(`N' - number of seconds)")
+    AP_SRV_CMD(Log, TAKE1,
+               "SSL logfile for SSL-related messages "
+               "(`/path/to/file', `|/path/to/program')")
+    AP_SRV_CMD(LogLevel, TAKE1,
+               "SSL logfile verbosity level "
+               "(`none', `error', `warn', `info', `debug')")
+    AP_SRV_CMD(Protocol, RAW_ARGS,
+               "Enable or disable various SSL protocols"
+               "(`[+-][SSLv2|SSLv3|TLSv1] ...' - see manual)")
+
+#ifdef SSL_EXPERIMENTAL_PROXY
+    /* 
+     * Proxy configuration for remote SSL connections
+     */
+    AP_SRV_CMD(ProxyProtocol, RAW_ARGS,
+               "SSL Proxy: enable or disable SSL protocol flavors "
+               "(`[+-][SSLv2|SSLv3|TLSv1] ...' - see manual)")
+    AP_SRV_CMD(ProxyCipherSuite, TAKE1,
+               "SSL Proxy: colon-delimited list of permitted SSL ciphers "
+               "(`XXX:...:XXX' - see manual)")
+    AP_SRV_CMD(ProxyVerify, FLAG,
+               "SSL Proxy: whether to verify the remote certificate "
+               "(`on' or `off')")
+    AP_SRV_CMD(ProxyVerifyDepth, TAKE1,
+               "SSL Proxy: maximum certificate verification depth "
+               "(`N' - number of intermediate certificates)")
+    AP_SRV_CMD(ProxyCACertificateFile, TAKE1,
+               "SSL Proxy: file containing server certificates "
+               "(`/path/to/file' - PEM encoded certificates)")
+    AP_SRV_CMD(ProxyCACertificatePath, TAKE1,
+               "SSL Proxy: directory containing server certificates "
+               "(`/path/to/dir' - contains PEM encoded certificates)")
+    AP_SRV_CMD(ProxyMachineCertificateFile, TAKE1,
+               "SSL Proxy: file containing client certificates "
+               "(`/path/to/file' - PEM encoded certificates)")
+    AP_SRV_CMD(ProxyMachineCertificatePath, TAKE1,
+               "SSL Proxy: directory containing client certificates "
+               "(`/path/to/dir' - contains PEM encoded certificates)")
+#endif
+
+    /*
+     * Per-directory context configuration directives
+     */
+    AP_DIR_CMD(Options, OPTIONS, RAW_ARGS,
+               "Set one of more options to configure the SSL engine"
+               "(`[+-]option[=value] ...' - see manual)")
+    AP_DIR_CMD(RequireSSL, AUTHCFG, NO_ARGS,
+               "Require the SSL protocol for the per-directory context "
+               "(no arguments)")
+    AP_DIR_CMD(Require, AUTHCFG, RAW_ARGS,
+               "Require a boolean expresion to evaluate to true for granting access"
+               "(arbitrary complex boolean expression - see manual)")
+
+    AP_END_CMD
+};
+
+static const handler_rec ssl_config_handler[] = {
+    { "mod_ssl:content-handler", ssl_hook_Handler },
+    { NULL, NULL }
+};
+
+/*
+ *  the main Apache API config structure
+ */
+module MODULE_VAR_EXPORT ssl_module = {
+    STANDARD_MODULE_STUFF,
+
+    /* Standard API (always present) */
+
+    ssl_init_Module,          /* module initializer                  */
+    ssl_config_perdir_create, /* create per-dir    config structures */
+    ssl_config_perdir_merge,  /* merge  per-dir    config structures */
+    ssl_config_server_create, /* create per-server config structures */
+    ssl_config_server_merge,  /* merge  per-server config structures */
+    ssl_config_cmds,          /* table of config file commands       */
+    ssl_config_handler,       /* [#8] MIME-typed-dispatched handlers */
+    ssl_hook_Translate,       /* [#1] URI to filename translation    */
+    ssl_hook_Auth,            /* [#4] validate user id from request  */
+    ssl_hook_UserCheck,       /* [#5] check if the user is ok _here_ */
+    ssl_hook_Access,          /* [#3] check access by host address   */
+    NULL,                     /* [#6] determine MIME type            */
+    ssl_hook_Fixup,           /* [#7] pre-run fixups                 */
+    NULL,                     /* [#9] log a transaction              */
+    NULL,                     /* [#2] header parser                  */
+    ssl_init_Child,           /* child_init                          */
+    NULL,                     /* child_exit                          */
+    ssl_hook_ReadReq,         /* [#0] post read-request              */
+
+    /* Extended API (forced to be enabled with mod_ssl) */
+
+    ssl_hook_AddModule,       /* after modules was added to core     */
+    ssl_hook_RemoveModule,    /* before module is removed from core  */
+    ssl_hook_RewriteCommand,  /* configuration command rewriting     */
+    ssl_hook_NewConnection,   /* socket connection open              */
+    ssl_hook_CloseConnection  /* socket connection close             */
+};
+
diff --git a/modules/ssl/mod_ssl.h b/modules/ssl/mod_ssl.h
new file mode 100644 (file)
index 0000000..8731ef9
--- /dev/null
@@ -0,0 +1,854 @@
+/*                      _             _
+**  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
+** | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
+** | | | | | | (_) | (_| |   \__ \__ \ |  www.modssl.org
+** |_| |_| |_|\___/ \__,_|___|___/___/_|  ftp.modssl.org
+**                      |_____|
+**  mod_ssl.h
+**  Global header
+*/
+
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ *    nor may "mod_ssl" appear in their names without prior
+ *    written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+                             /* ``The Apache Group: a collection
+                                  of talented individuals who are
+                                  trying to perfect the art of
+                                  never finishing something.''
+                                             -- Rob Hartill         */
+#ifndef MOD_SSL_H
+#define MOD_SSL_H 1
+
+/* 
+ * Check whether Extended API (EAPI) is enabled
+ */
+#ifndef EAPI
+#error "mod_ssl requires Extended API (EAPI)"
+#endif
+
+/* 
+ * Optionally enable the experimental stuff, but allow the user to
+ * override the decision which experimental parts are included by using
+ * CFLAGS="-DSSL_EXPERIMENTAL_xxxx_IGNORE".
+ */
+#ifdef SSL_EXPERIMENTAL
+#ifndef SSL_EXPERIMENTAL_PERDIRCA_IGNORE
+#define SSL_EXPERIMENTAL_PERDIRCA
+#endif
+#ifndef SSL_EXPERIMENTAL_PROXY_IGNORE
+#define SSL_EXPERIMENTAL_PROXY
+#endif
+#ifdef SSL_ENGINE
+#ifndef SSL_EXPERIMENTAL_ENGINE_IGNORE
+#define SSL_EXPERIMENTAL_ENGINE
+#endif
+#endif
+#endif /* SSL_EXPERIMENTAL */
+
+/*
+ * Power up our brain...
+ */
+
+/* OS headers */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <time.h>
+#ifndef WIN32
+#include <sys/time.h>
+#endif
+#ifdef WIN32
+#include <wincrypt.h>
+#include <winsock2.h>
+#endif
+
+/* OpenSSL headers */
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+#include <openssl/pem.h>
+#include <openssl/crypto.h>
+#include <openssl/evp.h>
+#include <openssl/rand.h>
+#ifdef SSL_EXPERIMENTAL_ENGINE
+#include <openssl/engine.h>
+#endif
+
+/* Apache headers */
+#define CORE_PRIVATE
+#include "ap_config.h"
+#include "httpd.h"
+#include "http_config.h"
+#include "http_conf_globals.h"
+#include "http_protocol.h"
+#include "http_request.h"
+#include "http_main.h"
+#include "http_core.h"
+#include "http_log.h"
+#include "scoreboard.h"
+#include "util_md5.h"
+#include "fnmatch.h"
+#undef CORE_PRIVATE
+
+/* mod_ssl headers */
+#include "ssl_expr.h"
+#include "ssl_util_ssl.h"
+#include "ssl_util_table.h"
+
+/*
+ * Provide reasonable default for some defines
+ */
+#ifndef FALSE
+#define FALSE (0)
+#endif
+#ifndef TRUE
+#define TRUE (!FALSE)
+#endif
+#ifndef PFALSE
+#define PFALSE ((void *)FALSE)
+#endif
+#ifndef PTRUE
+#define PTRUE ((void *)TRUE)
+#endif
+#ifndef UNSET
+#define UNSET (-1)
+#endif
+#ifndef NUL
+#define NUL '\0'
+#endif
+#ifndef RAND_MAX
+#include <limits.h>
+#define RAND_MAX INT_MAX
+#endif
+
+/*
+ * Provide reasonable defines for some types
+ */
+#ifndef BOOL
+#define BOOL unsigned int
+#endif
+#ifndef UCHAR
+#define UCHAR unsigned char
+#endif
+
+/*
+ * Provide useful shorthands
+ */
+#define strEQ(s1,s2)     (strcmp(s1,s2)        == 0)
+#define strNE(s1,s2)     (strcmp(s1,s2)        != 0)
+#define strEQn(s1,s2,n)  (strncmp(s1,s2,n)     == 0)
+#define strNEn(s1,s2,n)  (strncmp(s1,s2,n)     != 0)
+
+#define strcEQ(s1,s2)    (strcasecmp(s1,s2)    == 0)
+#define strcNE(s1,s2)    (strcasecmp(s1,s2)    != 0)
+#define strcEQn(s1,s2,n) (strncasecmp(s1,s2,n) == 0)
+#define strcNEn(s1,s2,n) (strncasecmp(s1,s2,n) != 0)
+
+#define strIsEmpty(s)    (s == NULL || s[0] == NUL)
+
+#define cfgMerge(el,unset)  new->el = add->el == unset ? base->el : add->el
+#define cfgMergeArray(el)   new->el = ap_append_arrays(p, add->el, base->el)
+#define cfgMergeTable(el)   new->el = ap_overlay_tables(p, add->el, base->el)
+#define cfgMergeCtx(el)     new->el = ap_ctx_overlay(p, add->el, base->el)
+#define cfgMergeString(el)  cfgMerge(el, NULL)
+#define cfgMergeBool(el)    cfgMerge(el, UNSET)
+#define cfgMergeInt(el)     cfgMerge(el, UNSET)
+
+#define myModConfig()    (SSLModConfigRec *)ap_ctx_get(ap_global_ctx, "ssl_module")
+#define mySrvConfig(srv) (SSLSrvConfigRec *)ap_get_module_config(srv->module_config,  &ssl_module)
+#define myDirConfig(req) (SSLDirConfigRec *)ap_get_module_config(req->per_dir_config, &ssl_module)
+
+#define myCtxVarSet(mc,num,val)  mc->rCtx.pV##num = val
+#define myCtxVarGet(mc,num,type) (type)(mc->rCtx.pV##num)
+
+#define AP_ALL_CMD(name, args, desc) \
+        { "SSL"#name, ssl_cmd_SSL##name, NULL, RSRC_CONF|OR_AUTHCFG, args, desc },
+#define AP_SRV_CMD(name, args, desc) \
+        { "SSL"#name, ssl_cmd_SSL##name, NULL, RSRC_CONF, args, desc },
+#define AP_DIR_CMD(name, type, args, desc) \
+        { "SSL"#name, ssl_cmd_SSL##name, NULL, OR_##type, args, desc },
+#define AP_END_CMD \
+        { NULL }
+
+/*
+ * SSL Logging
+ */
+#define SSL_LOG_NONE    (1<<0)
+#define SSL_LOG_ERROR   (1<<1)
+#define SSL_LOG_WARN    (1<<2)
+#define SSL_LOG_INFO    (1<<3)
+#define SSL_LOG_TRACE   (1<<4)
+#define SSL_LOG_DEBUG   (1<<5)
+#define SSL_LOG_MASK    (SSL_LOG_ERROR|SSL_LOG_WARN|SSL_LOG_INFO|SSL_LOG_TRACE|SSL_LOG_DEBUG)
+
+#define SSL_ADD_NONE     (1<<8)
+#define SSL_ADD_ERRNO    (1<<9)
+#define SSL_ADD_SSLERR   (1<<10)
+#define SSL_NO_TIMESTAMP (1<<11)
+#define SSL_NO_LEVELID   (1<<12)
+#define SSL_NO_NEWLINE   (1<<13)
+
+/*
+ * Defaults for the configuration
+ */
+#ifndef SSL_SESSION_CACHE_TIMEOUT
+#define SSL_SESSION_CACHE_TIMEOUT  300
+#endif
+
+/*
+ * Support for file locking: Try to determine whether we should use fcntl() or
+ * flock().  Would be better ap_config.h could provide this... :-(
+  */
+#if defined(USE_FCNTL_SERIALIZED_ACCEPT)
+#define SSL_USE_FCNTL 1
+#include <fcntl.h>
+#endif
+#if defined(USE_FLOCK_SERIALIZED_ACCEPT)
+#define SSL_USE_FLOCK 1
+#include <sys/file.h>
+#endif
+#if !defined(SSL_USE_FCNTL) && !defined(SSL_USE_FLOCK)
+#define SSL_USE_FLOCK 1
+#if !defined(MPE) && !defined(WIN32)
+#include <sys/file.h>
+#endif
+#ifndef LOCK_UN
+#undef SSL_USE_FLOCK
+#define SSL_USE_FCNTL 1
+#include <fcntl.h>
+#endif
+#endif
+#ifdef AIX
+#undef SSL_USE_FLOCK
+#define SSL_USE_FCNTL 1
+#include <fcntl.h>
+#endif
+
+/*
+ * Support for Mutex
+ */
+#ifndef WIN32
+#define SSL_MUTEX_LOCK_MODE ( S_IRUSR|S_IWUSR )
+#else
+#define SSL_MUTEX_LOCK_MODE (_S_IREAD|_S_IWRITE )
+#endif
+#if defined(USE_SYSVSEM_SERIALIZED_ACCEPT) ||\
+    (defined(__FreeBSD__) && defined(__FreeBSD_version) &&\
+     __FreeBSD_version >= 300000) ||\
+    (defined(LINUX) && defined(__GLIBC__) && defined(__GLIBC_MINOR__) &&\
+     LINUX >= 2 && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1) ||\
+    defined(SOLARIS2) || defined(__hpux) ||\
+    (defined (__digital__) && defined (__unix__))
+#define SSL_CAN_USE_SEM
+#define SSL_HAVE_IPCSEM
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/sem.h>
+/* 
+ * Some platforms have a `union semun' pre-defined but Single Unix
+ * Specification (SUSv2) says in semctl(2): `If required, it is of
+ * type union semun, which the application program must explicitly
+ * declare'. So we define it always ourself to avoid problems (but under
+ * a different name to avoid a namespace clash).
+ */
+union ssl_ipc_semun {
+    long val;
+    struct semid_ds *buf;
+    unsigned short int *array;
+};
+#endif
+#ifdef WIN32
+#define SSL_CAN_USE_SEM
+#define SSL_HAVE_W32SEM
+#include "multithread.h"
+#include <process.h>
+#endif
+
+/*
+ * Support for MM library
+ */
+#ifndef WIN32
+#define SSL_MM_FILE_MODE ( S_IRUSR|S_IWUSR )
+#else
+#define SSL_MM_FILE_MODE ( _S_IREAD|_S_IWRITE )
+#endif
+
+/*
+ * Support for DBM library
+ */
+#ifndef WIN32
+#define SSL_DBM_FILE_MODE ( S_IRUSR|S_IWUSR )
+#else
+#define SSL_USE_SDBM
+#define SSL_DBM_FILE_MODE ( _S_IREAD|_S_IWRITE )
+#endif
+
+#ifdef SSL_USE_SDBM
+#include "ssl_util_sdbm.h"
+#define ssl_dbm_open     sdbm_open
+#define ssl_dbm_close    sdbm_close
+#define ssl_dbm_store    sdbm_store
+#define ssl_dbm_fetch    sdbm_fetch
+#define ssl_dbm_delete   sdbm_delete
+#define ssl_dbm_firstkey sdbm_firstkey
+#define ssl_dbm_nextkey  sdbm_nextkey
+#define SSL_DBM_FILE_SUFFIX_DIR ".dir"
+#define SSL_DBM_FILE_SUFFIX_PAG ".pag"
+#else /* !SSL_USE_SDBM */
+#if defined(__GLIBC__) && defined(__GLIBC_MINOR__) \
+    && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1
+#include <db1/ndbm.h>
+#else
+#include <ndbm.h>
+#endif
+#define ssl_dbm_open     dbm_open
+#define ssl_dbm_close    dbm_close
+#define ssl_dbm_store    dbm_store
+#define ssl_dbm_fetch    dbm_fetch
+#define ssl_dbm_delete   dbm_delete
+#define ssl_dbm_firstkey dbm_firstkey
+#define ssl_dbm_nextkey  dbm_nextkey
+#if !defined(SSL_DBM_FILE_SUFFIX_DIR) && !defined(SSL_DBM_FILE_SUFFIX_PAG)
+#if defined(DBM_SUFFIX)
+#define SSL_DBM_FILE_SUFFIX_DIR DBM_SUFFIX
+#define SSL_DBM_FILE_SUFFIX_PAG DBM_SUFFIX
+#elif defined(__FreeBSD__) || (defined(DB_LOCK) && defined(DB_SHMEM))
+#define SSL_DBM_FILE_SUFFIX_DIR ".db"
+#define SSL_DBM_FILE_SUFFIX_PAG ".db"
+#else
+#define SSL_DBM_FILE_SUFFIX_DIR ".dir"
+#define SSL_DBM_FILE_SUFFIX_PAG ".pag"
+#endif
+#endif
+#endif /* !SSL_USE_SDBM */
+
+/*
+ * Check for OpenSSL version 
+ */
+#if SSL_LIBRARY_VERSION < 0x00903100
+#error "mod_ssl requires OpenSSL 0.9.3 or higher"
+#endif
+
+/*
+ * The own data structures
+ */
+typedef struct {
+    pool *pPool;
+    pool *pSubPool;
+    array_header *aData;
+} ssl_ds_array;
+
+typedef struct {
+    pool *pPool;
+    pool *pSubPool;
+    array_header *aKey;
+    array_header *aData;
+} ssl_ds_table;
+
+/*
+ * Define the certificate algorithm types
+ */
+
+typedef int ssl_algo_t;
+
+#define SSL_ALGO_UNKNOWN (0)
+#define SSL_ALGO_RSA     (1<<0)
+#define SSL_ALGO_DSA     (1<<1)
+#define SSL_ALGO_ALL     (SSL_ALGO_RSA|SSL_ALGO_DSA)
+
+#define SSL_AIDX_RSA     (0)
+#define SSL_AIDX_DSA     (1)
+#define SSL_AIDX_MAX     (2)
+
+/*
+ * Define IDs for the temporary RSA keys and DH params
+ */
+
+#define SSL_TKP_GEN        (0)
+#define SSL_TKP_ALLOC      (1)
+#define SSL_TKP_FREE       (2)
+
+#define SSL_TKPIDX_RSA512  (0)
+#define SSL_TKPIDX_RSA1024 (1)
+#define SSL_TKPIDX_DH512   (2)
+#define SSL_TKPIDX_DH1024  (3)
+#define SSL_TKPIDX_MAX     (4)
+
+/*
+ * Define the SSL options
+ */
+#define SSL_OPT_NONE           (0)
+#define SSL_OPT_RELSET         (1<<0)
+#define SSL_OPT_STDENVVARS     (1<<1)
+#define SSL_OPT_COMPATENVVARS  (1<<2)
+#define SSL_OPT_EXPORTCERTDATA (1<<3)
+#define SSL_OPT_FAKEBASICAUTH  (1<<4)
+#define SSL_OPT_STRICTREQUIRE  (1<<5)
+#define SSL_OPT_OPTRENEGOTIATE (1<<6)
+#define SSL_OPT_ALL            (SSL_OPT_STDENVVARS|SSL_OPT_COMPATENVVAR|SSL_OPT_EXPORTCERTDATA|SSL_OPT_FAKEBASICAUTH|SSL_OPT_STRICTREQUIRE|SSL_OPT_OPTRENEGOTIATE)
+typedef int ssl_opt_t;
+
+/*
+ * Define the SSL Protocol options
+ */
+#define SSL_PROTOCOL_NONE  (0)
+#define SSL_PROTOCOL_SSLV2 (1<<0)
+#define SSL_PROTOCOL_SSLV3 (1<<1)
+#define SSL_PROTOCOL_TLSV1 (1<<2)
+#define SSL_PROTOCOL_ALL   (SSL_PROTOCOL_SSLV2|SSL_PROTOCOL_SSLV3|SSL_PROTOCOL_TLSV1)
+typedef int ssl_proto_t;
+
+/*
+ * Define the SSL verify levels
+ */
+typedef enum {
+    SSL_CVERIFY_UNSET           = UNSET,
+    SSL_CVERIFY_NONE            = 0,
+    SSL_CVERIFY_OPTIONAL        = 1,
+    SSL_CVERIFY_REQUIRE         = 2,
+    SSL_CVERIFY_OPTIONAL_NO_CA  = 3
+} ssl_verify_t;
+
+/*
+ * Define the SSL pass phrase dialog types
+ */
+typedef enum {
+    SSL_PPTYPE_UNSET   = UNSET,
+    SSL_PPTYPE_BUILTIN = 0,
+    SSL_PPTYPE_FILTER  = 1
+} ssl_pphrase_t;
+
+/*
+ * Define the Path Checking modes
+ */
+#define SSL_PCM_EXISTS     1
+#define SSL_PCM_ISREG      2
+#define SSL_PCM_ISDIR      4
+#define SSL_PCM_ISNONZERO  8
+typedef unsigned int ssl_pathcheck_t;
+
+/*
+ * Define the SSL session cache modes and structures
+ */
+typedef enum {
+    SSL_SCMODE_UNSET = UNSET,
+    SSL_SCMODE_NONE  = 0,
+    SSL_SCMODE_DBM   = 1,
+    SSL_SCMODE_SHMHT = 2,
+    SSL_SCMODE_SHMCB = 3
+} ssl_scmode_t;
+
+/*
+ * Define the SSL mutex modes
+ */
+typedef enum {
+    SSL_MUTEXMODE_UNSET  = UNSET,
+    SSL_MUTEXMODE_NONE   = 0,
+    SSL_MUTEXMODE_FILE   = 1,
+    SSL_MUTEXMODE_SEM    = 2
+} ssl_mutexmode_t;
+
+/*
+ * Define the SSL requirement structure
+ */
+typedef struct {
+    char     *cpExpr;
+    ssl_expr *mpExpr;
+} ssl_require_t;
+
+/*
+ * Define the SSL random number generator seeding source
+ */
+typedef enum {
+    SSL_RSCTX_STARTUP = 1,
+    SSL_RSCTX_CONNECT = 2
+} ssl_rsctx_t;
+typedef enum {
+    SSL_RSSRC_BUILTIN = 1,
+    SSL_RSSRC_FILE    = 2,
+    SSL_RSSRC_EXEC    = 3
+#if SSL_LIBRARY_VERSION >= 0x00905100
+   ,SSL_RSSRC_EGD     = 4
+#endif
+} ssl_rssrc_t;
+typedef struct {
+    ssl_rsctx_t  nCtx;
+    ssl_rssrc_t  nSrc;
+    char        *cpPath;
+    int          nBytes;
+} ssl_randseed_t;
+
+/*
+ * Define the structure of an ASN.1 anything
+ */
+typedef struct {
+    long int       nData;
+    unsigned char *cpData;
+} ssl_asn1_t;
+
+/*
+ * Define the mod_ssl per-module configuration structure
+ * (i.e. the global configuration for each httpd process)
+ */
+
+typedef struct {
+    pool           *pPool;
+    BOOL            bFixed;
+    int             nInitCount;
+    int             nSessionCacheMode;
+    char           *szSessionCacheDataFile;
+    int             nSessionCacheDataSize;
+    AP_MM          *pSessionCacheDataMM;
+    table_t        *tSessionCacheDataTable;
+    ssl_mutexmode_t nMutexMode;
+    char           *szMutexFile;
+    int             nMutexFD;
+    int             nMutexSEMID;
+    array_header   *aRandSeed;
+    ssl_ds_table   *tTmpKeys;
+    void           *pTmpKeys[SSL_TKPIDX_MAX];
+    ssl_ds_table   *tPublicCert;
+    ssl_ds_table   *tPrivateKey;
+#ifdef SSL_EXPERIMENTAL_ENGINE
+    char           *szCryptoDevice;
+#endif
+    struct {
+        void *pV1, *pV2, *pV3, *pV4, *pV5, *pV6, *pV7, *pV8, *pV9, *pV10;
+    } rCtx;
+#ifdef SSL_VENDOR
+    ap_ctx         *ctx;
+#endif
+} SSLModConfigRec;
+
+/*
+ * Define the mod_ssl per-server configuration structure
+ * (i.e. the configuration for the main server
+ *  and all <VirtualHost> contexts)
+ */
+typedef struct {
+    BOOL         bEnabled;
+    char        *szPublicCertFile[SSL_AIDX_MAX];
+    char        *szPrivateKeyFile[SSL_AIDX_MAX];
+    char        *szCertificateChain;
+    char        *szCACertificatePath;
+    char        *szCACertificateFile;
+    char        *szLogFile;
+    char        *szCipherSuite;
+    FILE        *fileLogFile;
+    int          nLogLevel;
+    int          nVerifyDepth;
+    ssl_verify_t nVerifyClient;
+    X509        *pPublicCert[SSL_AIDX_MAX];
+    EVP_PKEY    *pPrivateKey[SSL_AIDX_MAX];
+    SSL_CTX     *pSSLCtx;
+    int          nSessionCacheTimeout;
+    int          nPassPhraseDialogType;
+    char        *szPassPhraseDialogPath;
+    ssl_proto_t  nProtocol;
+    char        *szCARevocationPath;
+    char        *szCARevocationFile;
+    X509_STORE  *pRevocationStore;
+#ifdef SSL_EXPERIMENTAL_PROXY
+    /* Configuration details for proxy operation */
+    ssl_proto_t  nProxyProtocol;
+    int          bProxyVerify;
+    int          nProxyVerifyDepth;
+    char        *szProxyCACertificatePath;
+    char        *szProxyCACertificateFile;
+    char        *szProxyClientCertificateFile;
+    char        *szProxyClientCertificatePath;
+    char        *szProxyCipherSuite;
+    SSL_CTX     *pSSLProxyCtx;
+    STACK_OF(X509_INFO) *skProxyClientCerts;
+#endif
+#ifdef SSL_VENDOR
+    ap_ctx      *ctx;
+#endif
+} SSLSrvConfigRec;
+
+/*
+ * Define the mod_ssl per-directory configuration structure
+ * (i.e. the local configuration for all <Directory>
+ *  and .htaccess contexts)
+ */
+typedef struct {
+    BOOL          bSSLRequired;
+    array_header *aRequirement;
+    ssl_opt_t     nOptions;
+    ssl_opt_t     nOptionsAdd;
+    ssl_opt_t     nOptionsDel;
+    char         *szCipherSuite;
+    ssl_verify_t  nVerifyClient;
+    int           nVerifyDepth;
+#ifdef SSL_EXPERIMENTAL_PERDIRCA
+    char         *szCACertificatePath;
+    char         *szCACertificateFile;
+#endif
+#ifdef SSL_VENDOR
+    ap_ctx       *ctx;
+#endif
+} SSLDirConfigRec;
+
+/*
+ *  function prototypes
+ */
+
+/*  API glue structures  */
+extern module MODULE_VAR_EXPORT ssl_module;
+
+/*  configuration handling   */
+void         ssl_config_global_create(void);
+void         ssl_config_global_fix(void);
+BOOL         ssl_config_global_isfixed(void);
+void        *ssl_config_server_create(pool *, server_rec *);
+void        *ssl_config_server_merge(pool *, void *, void *);
+void        *ssl_config_perdir_create(pool *, char *);
+void        *ssl_config_perdir_merge(pool *, void *, void *);
+const char  *ssl_cmd_SSLMutex(cmd_parms *, char *, char *);
+const char  *ssl_cmd_SSLPassPhraseDialog(cmd_parms *, char *, char *);
+const char  *ssl_cmd_SSLCryptoDevice(cmd_parms *, char *, char *);
+const char  *ssl_cmd_SSLRandomSeed(cmd_parms *, char *, char *, char *, char *);
+const char  *ssl_cmd_SSLEngine(cmd_parms *, char *, int);
+const char  *ssl_cmd_SSLCipherSuite(cmd_parms *, SSLDirConfigRec *, char *);
+const char  *ssl_cmd_SSLCertificateFile(cmd_parms *, char *, char *);
+const char  *ssl_cmd_SSLCertificateKeyFile(cmd_parms *, char *, char *);
+const char  *ssl_cmd_SSLCertificateChainFile(cmd_parms *, char *, char *);
+const char  *ssl_cmd_SSLCACertificatePath(cmd_parms *, SSLDirConfigRec *, char *);
+const char  *ssl_cmd_SSLCACertificateFile(cmd_parms *, SSLDirConfigRec *, char *);
+const char  *ssl_cmd_SSLCARevocationPath(cmd_parms *, SSLDirConfigRec *, char *);
+const char  *ssl_cmd_SSLCARevocationFile(cmd_parms *, SSLDirConfigRec *, char *);
+const char  *ssl_cmd_SSLVerifyClient(cmd_parms *, SSLDirConfigRec *, char *);
+const char  *ssl_cmd_SSLVerifyDepth(cmd_parms *, SSLDirConfigRec *, char *);
+const char  *ssl_cmd_SSLSessionCache(cmd_parms *, char *, char *);
+const char  *ssl_cmd_SSLSessionCacheTimeout(cmd_parms *, char *, char *);
+const char  *ssl_cmd_SSLLog(cmd_parms *, char *, char *);
+const char  *ssl_cmd_SSLLogLevel(cmd_parms *, char *, char *);
+const char  *ssl_cmd_SSLProtocol(cmd_parms *, char *, const char *);
+const char  *ssl_cmd_SSLOptions(cmd_parms *, SSLDirConfigRec *, const char *);
+const char  *ssl_cmd_SSLRequireSSL(cmd_parms *, SSLDirConfigRec *, char *);
+const char  *ssl_cmd_SSLRequire(cmd_parms *, SSLDirConfigRec *, char *);
+#ifdef SSL_EXPERIMENTAL_PROXY
+const char  *ssl_cmd_SSLProxyProtocol(cmd_parms *, char *, const char *);
+const char  *ssl_cmd_SSLProxyCipherSuite(cmd_parms *, char *, char *);
+const char  *ssl_cmd_SSLProxyVerify(cmd_parms *, char *, int);
+const char  *ssl_cmd_SSLProxyVerifyDepth(cmd_parms *, char *, char *);
+const char  *ssl_cmd_SSLProxyCACertificatePath(cmd_parms *, char *, char *);
+const char  *ssl_cmd_SSLProxyCACertificateFile(cmd_parms *, char *, char *);
+const char  *ssl_cmd_SSLProxyMachineCertificatePath(cmd_parms *, char *, char *);
+const char  *ssl_cmd_SSLProxyMachineCertificateFile(cmd_parms *, char *, char *);
+#endif
+
+/*  module initialization  */
+void         ssl_init_Module(server_rec *, pool *);
+void         ssl_init_SSLLibrary(void);
+void         ssl_init_Engine(server_rec *, pool *);
+void         ssl_init_TmpKeysHandle(int, server_rec *, pool *);
+void         ssl_init_ConfigureServer(server_rec *, pool *, SSLSrvConfigRec *);
+void         ssl_init_CheckServers(server_rec *, pool *);
+STACK_OF(X509_NAME) 
+            *ssl_init_FindCAList(server_rec *, pool *, char *, char *);
+void         ssl_init_Child(server_rec *, pool *);
+void         ssl_init_ChildKill(void *);
+void         ssl_init_ModuleKill(void *);
+
+/*  Apache API hooks  */
+void         ssl_hook_AddModule(module *);
+void         ssl_hook_RemoveModule(module *);
+char        *ssl_hook_RewriteCommand(cmd_parms *, void *, const char *);
+void         ssl_hook_NewConnection(conn_rec *);
+void         ssl_hook_TimeoutConnection(int);
+void         ssl_hook_CloseConnection(conn_rec *);
+int          ssl_hook_Translate(request_rec *);
+int          ssl_hook_Auth(request_rec *);
+int          ssl_hook_UserCheck(request_rec *);
+int          ssl_hook_Access(request_rec *);
+int          ssl_hook_Fixup(request_rec *);
+int          ssl_hook_ReadReq(request_rec *);
+int          ssl_hook_Handler(request_rec *);
+
+/*  OpenSSL callbacks */
+RSA         *ssl_callback_TmpRSA(SSL *, int, int);
+DH          *ssl_callback_TmpDH(SSL *, int, int);
+int          ssl_callback_SSLVerify(int, X509_STORE_CTX *);
+int          ssl_callback_SSLVerify_CRL(int, X509_STORE_CTX *, server_rec *);
+int          ssl_callback_NewSessionCacheEntry(SSL *, SSL_SESSION *);
+SSL_SESSION *ssl_callback_GetSessionCacheEntry(SSL *, unsigned char *, int, int *);
+void         ssl_callback_DelSessionCacheEntry(SSL_CTX *, SSL_SESSION *);
+void         ssl_callback_LogTracingState(SSL *, int, int);
+
+/*  Session Cache Support  */
+void         ssl_scache_init(server_rec *, pool *);
+void         ssl_scache_kill(server_rec *);
+BOOL         ssl_scache_store(server_rec *, UCHAR *, int, time_t, SSL_SESSION *);
+SSL_SESSION *ssl_scache_retrieve(server_rec *, UCHAR *, int);
+void         ssl_scache_remove(server_rec *, UCHAR *, int);
+void         ssl_scache_expire(server_rec *);
+void         ssl_scache_status(server_rec *, pool *, void (*)(char *, void *), void *);
+char        *ssl_scache_id2sz(UCHAR *, int);
+void         ssl_scache_dbm_init(server_rec *, pool *);
+void         ssl_scache_dbm_kill(server_rec *);
+BOOL         ssl_scache_dbm_store(server_rec *, UCHAR *, int, time_t, SSL_SESSION *);
+SSL_SESSION *ssl_scache_dbm_retrieve(server_rec *, UCHAR *, int);
+void         ssl_scache_dbm_remove(server_rec *, UCHAR *, int);
+void         ssl_scache_dbm_expire(server_rec *);
+void         ssl_scache_dbm_status(server_rec *, pool *, void (*)(char *, void *), void *);
+void         ssl_scache_shmht_init(server_rec *, pool *);
+void         ssl_scache_shmht_kill(server_rec *);
+BOOL         ssl_scache_shmht_store(server_rec *, UCHAR *, int, time_t, SSL_SESSION *);
+SSL_SESSION *ssl_scache_shmht_retrieve(server_rec *, UCHAR *, int);
+void         ssl_scache_shmht_remove(server_rec *, UCHAR *, int);
+void         ssl_scache_shmht_expire(server_rec *);
+void         ssl_scache_shmht_status(server_rec *, pool *, void (*)(char *, void *), void *);
+void         ssl_scache_shmcb_init(server_rec *, pool *);
+void         ssl_scache_shmcb_kill(server_rec *);
+BOOL         ssl_scache_shmcb_store(server_rec *, UCHAR *, int, time_t, SSL_SESSION *);
+SSL_SESSION *ssl_scache_shmcb_retrieve(server_rec *, UCHAR *, int);
+void         ssl_scache_shmcb_remove(server_rec *, UCHAR *, int);
+void         ssl_scache_shmcb_expire(server_rec *);
+void         ssl_scache_shmcb_status(server_rec *, pool *, void (*)(char *, void *), void *);
+
+/*  Pass Phrase Support  */
+void         ssl_pphrase_Handle(server_rec *, pool *);
+int          ssl_pphrase_Handle_CB(char *, int, int);
+
+/*  Diffie-Hellman Parameter Support  */
+DH           *ssl_dh_GetTmpParam(int);
+DH           *ssl_dh_GetParamFromFile(char *);
+
+/*  Data Structures */
+ssl_ds_array *ssl_ds_array_make(pool *, int);
+BOOL          ssl_ds_array_isempty(ssl_ds_array *);
+void         *ssl_ds_array_push(ssl_ds_array *);
+void         *ssl_ds_array_get(ssl_ds_array *, int);
+void          ssl_ds_array_wipeout(ssl_ds_array *);
+void          ssl_ds_array_kill(ssl_ds_array *);
+ssl_ds_table *ssl_ds_table_make(pool *, int);
+BOOL          ssl_ds_table_isempty(ssl_ds_table *);
+void         *ssl_ds_table_push(ssl_ds_table *, char *);
+void         *ssl_ds_table_get(ssl_ds_table *, char *);
+void          ssl_ds_table_wipeout(ssl_ds_table *);
+void          ssl_ds_table_kill(ssl_ds_table *);
+
+/*  Mutex Support  */
+void         ssl_mutex_init(server_rec *, pool *);
+void         ssl_mutex_reinit(server_rec *, pool *);
+void         ssl_mutex_on(server_rec *);
+void         ssl_mutex_off(server_rec *);
+void         ssl_mutex_kill(server_rec *s);
+void         ssl_mutex_file_create(server_rec *, pool *);
+void         ssl_mutex_file_open(server_rec *, pool *);
+void         ssl_mutex_file_remove(void *);
+BOOL         ssl_mutex_file_acquire(void);
+BOOL         ssl_mutex_file_release(void);
+void         ssl_mutex_sem_create(server_rec *, pool *);
+void         ssl_mutex_sem_open(server_rec *, pool *);
+void         ssl_mutex_sem_remove(void *);
+BOOL         ssl_mutex_sem_acquire(void);
+BOOL         ssl_mutex_sem_release(void);
+
+/*  Logfile Support  */
+void         ssl_log_open(server_rec *, server_rec *, pool *);
+BOOL         ssl_log_applies(server_rec *, int);
+void         ssl_log(server_rec *, int, const char *, ...);
+void         ssl_die(void);
+
+/*  Variables  */
+void         ssl_var_register(void);
+void         ssl_var_unregister(void);
+char        *ssl_var_lookup(pool *, server_rec *, conn_rec *, request_rec *, char *);
+
+/*  I/O  */
+void         ssl_io_register(void);
+void         ssl_io_unregister(void);
+long         ssl_io_data_cb(BIO *, int, const char *, int, long, long);
+#ifndef SSL_CONSERVATIVE
+void         ssl_io_suck(request_rec *, SSL *);
+#endif
+
+/*  PRNG  */
+int          ssl_rand_seed(server_rec *, pool *, ssl_rsctx_t, char *);
+
+/*  Extensions  */
+void         ssl_ext_register(void);
+void         ssl_ext_unregister(void);
+
+/*  Compatibility  */
+#ifdef SSL_COMPAT
+char        *ssl_compat_directive(server_rec *, pool *, const char *);
+void         ssl_compat_variables(request_rec *);
+#endif
+
+/*  Utility Functions  */
+char        *ssl_util_server_root_relative(pool *, char *, char *);
+char        *ssl_util_vhostid(pool *, server_rec *);
+void         ssl_util_strupper(char *);
+void         ssl_util_uuencode(char *, const char *, BOOL);
+void         ssl_util_uuencode_binary(unsigned char *, const unsigned char *, int, BOOL);
+FILE        *ssl_util_ppopen(server_rec *, pool *, char *);
+int          ssl_util_ppopen_child(void *, child_info *);
+void         ssl_util_ppclose(server_rec *, pool *, FILE *);
+char        *ssl_util_readfilter(server_rec *, pool *, char *);
+BOOL         ssl_util_path_check(ssl_pathcheck_t, char *);
+ssl_algo_t   ssl_util_algotypeof(X509 *, EVP_PKEY *); 
+char        *ssl_util_algotypestr(ssl_algo_t);
+char        *ssl_util_ptxtsub(pool *, const char *, const char *, char *);
+void         ssl_util_thread_setup(void);
+
+/*  Vendor extension support  */
+#if defined(SSL_VENDOR) && defined(SSL_VENDOR_OBJS)
+void         ssl_vendor_register(void);
+void         ssl_vendor_unregister(void);
+#endif
+
+#endif /* MOD_SSL_H */
diff --git a/modules/ssl/ssl_engine_compat.c b/modules/ssl/ssl_engine_compat.c
new file mode 100644 (file)
index 0000000..a9fe50f
--- /dev/null
@@ -0,0 +1,511 @@
+/*                      _             _
+**  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
+** | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
+** | | | | | | (_) | (_| |   \__ \__ \ |  www.modssl.org
+** |_| |_| |_|\___/ \__,_|___|___/___/_|  ftp.modssl.org
+**                      |_____|
+**  ssl_engine_compat.c
+**  Backward Compatibility
+*/
+
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ *    nor may "mod_ssl" appear in their names without prior
+ *    written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+                             /* ``Backward compatibility is for
+                                  users who don't want to live
+                                  on the bleeding edge.''
+                                            -- Unknown          */
+#ifdef SSL_COMPAT
+
+#include "mod_ssl.h"
+
+
+/*  _________________________________________________________________
+**
+**  Backward Compatibility
+**  _________________________________________________________________
+*/
+
+/*
+ * The mapping of obsolete directives to official ones...
+ */
+
+static char *ssl_compat_RequireSSL(pool *, const char *, const char *, const char *);
+static char *ssl_compat_SSLSessionLockFile(pool *, const char *, const char *, const char *);
+static char *ssl_compat_SSLCacheDisable(pool *, const char *, const char *, const char *);
+static char *ssl_compat_SSLRequireCipher(pool *, const char *, const char *, const char *);
+static char *ssl_compat_SSLBanCipher(pool *, const char *, const char *, const char *);
+static char *ssl_compat_SSL_SessionDir(pool *, const char *, const char *, const char *);
+static char *ssl_compat_words2list(pool *, const char *);
+
+#define CRM_BEGIN              /* nop */
+#define CRM_ENTRY(what,action) { what, action },
+#define CRM_END                { NULL, NULL, NULL, NULL, NULL, NULL }
+#define CRM_CMD(cmd)           cmd, NULL, NULL
+#define CRM_STR(str)           NULL, str, NULL
+#define CRM_PAT(cmd)           NULL, NULL, pat
+#define CRM_LOG(msg)           msg, NULL, NULL
+#define CRM_SUB(new)           NULL, new, NULL
+#define CRM_CAL(fct)           NULL, NULL, fct
+
+static struct {
+    char *cpCommand;
+    char *cpSubstring;
+    char *cpPattern;
+    char *cpMessage;
+    char *cpSubst;
+    char *(*fpSubst)(pool *, const char *, const char *, const char *);
+} ssl_cmd_rewrite_map[] = {
+    CRM_BEGIN
+
+    /*
+     * Apache-SSL 1.x & mod_ssl 2.0.x backward compatibility
+     */
+    CRM_ENTRY( CRM_CMD("SSLEnable"),                   CRM_SUB("SSLEngine on")                )
+    CRM_ENTRY( CRM_CMD("SSLDisable"),                  CRM_SUB("SSLEngine off")               )
+    CRM_ENTRY( CRM_CMD("SSLLogFile"),                  CRM_SUB("SSLLog")                      )
+    CRM_ENTRY( CRM_CMD("SSLRequiredCiphers"),          CRM_SUB("SSLCipherSuite")              )
+    CRM_ENTRY( CRM_CMD("SSLRequireCipher"),            CRM_CAL(ssl_compat_SSLRequireCipher)   )
+    CRM_ENTRY( CRM_CMD("SSLBanCipher"),                CRM_CAL(ssl_compat_SSLBanCipher)       )
+    CRM_ENTRY( CRM_CMD("SSLFakeBasicAuth"),            CRM_SUB("SSLOptions +FakeBasicAuth")   )
+    CRM_ENTRY( CRM_CMD("SSLCacheServerPath"),          CRM_LOG("Use SSLSessionCache instead") )
+    CRM_ENTRY( CRM_CMD("SSLCacheServerPort"),          CRM_LOG("Use SSLSessionCache instead") )
+
+    /*
+     * Apache-SSL 1.x backward compatibility
+     */
+    CRM_ENTRY( CRM_CMD("SSLExportClientCertificates"), CRM_SUB("SSLOptions +ExportCertData")  )
+    CRM_ENTRY( CRM_CMD("SSLCacheServerRunDir"),        CRM_LOG("Not needed for mod_ssl")      )
+
+    /*
+     * Sioux 1.x backward compatibility
+     */
+    CRM_ENTRY( CRM_CMD("SSL_CertFile"),            CRM_SUB("SSLCertificateFile")              )
+    CRM_ENTRY( CRM_CMD("SSL_KeyFile"),             CRM_SUB("SSLCertificateKeyFile")           )
+    CRM_ENTRY( CRM_CMD("SSL_CipherSuite"),         CRM_SUB("SSLCipherSuite")                  )
+    CRM_ENTRY( CRM_CMD("SSL_X509VerifyDir"),       CRM_SUB("SSLCACertificatePath")            )
+    CRM_ENTRY( CRM_CMD("SSL_Log"),                 CRM_SUB("SSLLogFile")                      )
+    CRM_ENTRY( CRM_CMD("SSL_Connect"),             CRM_SUB("SSLEngine")                       )
+    CRM_ENTRY( CRM_CMD("SSL_ClientAuth"),          CRM_SUB("SSLVerifyClient")                 )
+    CRM_ENTRY( CRM_CMD("SSL_X509VerifyDepth"),     CRM_SUB("SSLVerifyDepth")                  )
+    CRM_ENTRY( CRM_CMD("SSL_FetchKeyPhraseFrom"),  CRM_LOG("Use SSLPassPhraseDialog instead") )
+    CRM_ENTRY( CRM_CMD("SSL_SessionDir"),          CRM_CAL(ssl_compat_SSL_SessionDir)         )
+    CRM_ENTRY( CRM_CMD("SSL_Require"),             CRM_LOG("Use SSLRequire instead (Syntax!)"))
+    CRM_ENTRY( CRM_CMD("SSL_CertFileType"),        CRM_LOG("Not supported by mod_ssl")        )
+    CRM_ENTRY( CRM_CMD("SSL_KeyFileType"),         CRM_LOG("Not supported by mod_ssl")        )
+    CRM_ENTRY( CRM_CMD("SSL_X509VerifyPolicy"),    CRM_LOG("Not supported by mod_ssl")        )
+    CRM_ENTRY( CRM_CMD("SSL_LogX509Attributes"),   CRM_LOG("Not supported by mod_ssl")        )
+
+    /*
+     * Stronghold 2.x backward compatibility
+     */
+    CRM_ENTRY( CRM_CMD("StrongholdAccelerator"),     CRM_LOG("Not supported by mod_ssl")      )
+    CRM_ENTRY( CRM_CMD("StrongholdKey"),             CRM_LOG("Not supported by mod_ssl")      )
+    CRM_ENTRY( CRM_CMD("StrongholdLicenseFile"),     CRM_LOG("Not supported by mod_ssl")      )
+    CRM_ENTRY( CRM_CMD("SSLFlag"),                   CRM_SUB("SSLEngine")                     )
+    CRM_ENTRY( CRM_CMD("SSLClientCAfile"),           CRM_SUB("SSLCACertificateFile")          )
+    CRM_ENTRY( CRM_CMD("SSLSessionLockFile"),        CRM_CAL(ssl_compat_SSLSessionLockFile)   )
+    CRM_ENTRY( CRM_CMD("SSLCacheDisable"),           CRM_CAL(ssl_compat_SSLCacheDisable)      )
+    CRM_ENTRY( CRM_CMD("RequireSSL"),                CRM_CAL(ssl_compat_RequireSSL)           )
+    CRM_ENTRY( CRM_CMD("SSLCipherList"),             CRM_SUB("SSLCipherSuite")                )
+    CRM_ENTRY( CRM_CMD("SSLErrorFile"),              CRM_LOG("Not needed for mod_ssl")        )
+    CRM_ENTRY( CRM_CMD("SSLRoot"),                   CRM_LOG("Not supported by mod_ssl")      )
+    CRM_ENTRY( CRM_CMD("SSL_CertificateLogDir"),     CRM_LOG("Not supported by mod_ssl")      )
+    CRM_ENTRY( CRM_CMD("AuthCertDir"),               CRM_LOG("Not supported by mod_ssl")      )
+    CRM_ENTRY( CRM_CMD("SSL_Group"),                 CRM_LOG("Not supported by mod_ssl")      )
+#ifndef SSL_EXPERIMENTAL_PROXY
+    CRM_ENTRY( CRM_CMD("SSLProxyMachineCertPath"),   CRM_LOG("Not supported by mod_ssl")      )
+    CRM_ENTRY( CRM_CMD("SSLProxyMachineCertFile"),   CRM_LOG("Not supported by mod_ssl")      )
+    CRM_ENTRY( CRM_CMD("SSLProxyCACertificatePath"), CRM_LOG("Not supported by mod_ssl")      )
+    CRM_ENTRY( CRM_CMD("SSLProxyCACertificateFile"), CRM_LOG("Not supported by mod_ssl")      )
+    CRM_ENTRY( CRM_CMD("SSLProxyVerifyDepth"),       CRM_LOG("Not supported by mod_ssl")      )
+    CRM_ENTRY( CRM_CMD("SSLProxyCipherList"),        CRM_LOG("Not supported by mod_ssl")      )
+#else
+    CRM_ENTRY( CRM_CMD("SSLProxyCipherList"),        CRM_SUB("SSLProxyCipherSuite")           )
+#endif
+
+    CRM_END
+};
+
+static char *ssl_compat_RequireSSL(
+    pool *p, const char *oline, const char *cmd, const char *args)
+{
+    char *cp;
+    
+    for (cp = (char *)args; ap_isspace(*cp); cp++)
+        ;
+    if (strcEQ(cp, "on"))
+        return "SSLRequireSSL";
+    return "";
+}
+
+static char *ssl_compat_SSLSessionLockFile(
+    pool *p, const char *oline, const char *cmd, const char *args)
+{
+    char *cp;
+
+    for (cp = (char *)args; ap_isspace(*cp); cp++)
+        ;
+    return ap_pstrcat(p, "SSLMutex file:", cp, NULL);
+}
+
+static char *ssl_compat_SSLCacheDisable(
+    pool *p, const char *oline, const char *cmd, const char *args)
+{
+    char *cp;
+
+    for (cp = (char *)args; ap_isspace(*cp); cp++)
+        ;
+    if (strcEQ(cp, "on"))
+        return "SSLSessionCache none";
+    return "";
+}
+
+static char *ssl_compat_SSLRequireCipher(pool *p, const char *oline, const char *cmd, const char *args)
+{
+    return ap_pstrcat(p, "SSLRequire %{SSL_CIPHER} in {",
+                          ssl_compat_words2list(p, args),
+                          "}", NULL);
+}
+
+static char *ssl_compat_SSLBanCipher(pool *p, const char *oline, const char *cmd, const char *args)
+{
+    return ap_pstrcat(p, "SSLRequire not (%{SSL_CIPHER} in {",
+                          ssl_compat_words2list(p, args),
+                          "})", NULL);
+}
+
+static char *ssl_compat_SSL_SessionDir(
+    pool *p, const char *oline, const char *cmd, const char *args)
+{
+    char *cp;
+   
+    for (cp = (char *)args; ap_isspace(*cp); cp++)
+        ;
+    return ap_pstrcat(p, "SSLSessionCache dir:", cp, NULL);
+}
+
+static char *ssl_compat_words2list(pool *p, const char *oline)
+{
+    char *line;
+    char *cpB;
+    char *cpE;
+    char *cpI;
+    char *cpO;
+    char n;
+
+    /*
+     * Step 1: Determine borders
+     */
+    cpB = (char *)oline;
+    while (*cpB == ' ' || *cpB == '\t')
+       cpB++;
+    cpE = cpB+strlen(cpB);
+    while (cpE > cpB && (*(cpE-1) == ' ' || *(cpE-1) == '\t'))
+        cpE--;
+
+    /*
+     * Step 2: Determine final size and allocate buffer
+     */
+    for (cpI = cpB, n = 1; cpI < cpE; cpI++)
+        if ((*cpI == ' ' || *cpI == '\t') &&
+            (cpI > cpB && *(cpI-1) != ' ' && *(cpI-1) != '\t'))
+            n++;
+    line = ap_palloc(p, (cpE-cpB)+(n*2)+n+1);
+    cpI = cpB;
+    cpO = line;
+    while (cpI < cpE) {
+        if (   (*cpI != ' ' && *cpI != '\t')
+            && (   cpI == cpB
+                || (   cpI > cpB
+                    && (*(cpI-1) == ' ' || *(cpI-1) == '\t')))) {
+            *cpO++ = '"';
+            *cpO++ = *cpI++;
+        }
+        else if (   (*cpI == ' ' || *cpI == '\t')
+                 && (   cpI > cpB
+                     && (*(cpI-1) != ' ' && *(cpI-1) != '\t'))) {
+            *cpO++ = '"';
+            *cpO++ = ',';
+            *cpO++ = *cpI++;
+        }
+        else {
+            *cpO++ = *cpI++;
+        }
+    }
+    if (cpI > cpB && (*(cpI-1) != ' ' && *(cpI-1) != '\t'))
+        *cpO++ = '"';
+    *cpO++ = NUL;
+    return line;
+}
+
+char *ssl_compat_directive(server_rec *s, pool *p, const char *oline)
+{
+    int i;
+    char *line;
+    char *cp;
+    char caCmd[1024];
+    char *cpArgs;
+    int match;
+
+    /*
+     * Skip comment lines
+     */
+    cp = (char *)oline;
+    while ((*cp == ' ' || *cp == '\t' || *cp == '\n') && (*cp != NUL))
+        cp++;
+    if (*cp == '#' || *cp == NUL)
+        return NULL;
+
+    /*
+     * Extract directive name
+     */
+    cp = (char *)oline;
+    for (i = 0; *cp != ' ' && *cp != '\t' && *cp != NUL && i < 1024; )
+        caCmd[i++] = *cp++;
+    caCmd[i] = NUL;
+    cpArgs = cp;
+
+    /*
+     * Apply rewriting map
+     */
+    line = NULL;
+    for (i = 0; !(ssl_cmd_rewrite_map[i].cpCommand == NULL &&
+                  ssl_cmd_rewrite_map[i].cpPattern == NULL   ); i++) {
+        /*
+         * Matching
+         */
+        match = FALSE;
+        if (ssl_cmd_rewrite_map[i].cpCommand != NULL) {
+            if (strcEQ(ssl_cmd_rewrite_map[i].cpCommand, caCmd))
+                match = TRUE;
+        }
+        else if (ssl_cmd_rewrite_map[i].cpSubstring != NULL) {
+            if (strstr(oline, ssl_cmd_rewrite_map[i].cpSubstring) != NULL)
+                match = TRUE;
+        }
+        else if (ssl_cmd_rewrite_map[i].cpPattern != NULL) {
+            if (ap_fnmatch(ssl_cmd_rewrite_map[i].cpPattern, oline, 0))
+                match = TRUE;
+        }
+
+        /*
+         * Action Processing
+         */
+        if (match) {
+            if (ssl_cmd_rewrite_map[i].cpMessage != NULL) {
+                ap_log_error(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, s,
+                             "mod_ssl:Compat: OBSOLETE '%s' => %s",
+                             oline, ssl_cmd_rewrite_map[i].cpMessage);
+                line = "";
+                break;
+            }
+            else if (ssl_cmd_rewrite_map[i].cpSubst != NULL) {
+                if (ssl_cmd_rewrite_map[i].cpCommand != NULL)
+                    line = ap_pstrcat(p, ssl_cmd_rewrite_map[i].cpSubst,
+                                      cpArgs, NULL);
+                else if (ssl_cmd_rewrite_map[i].cpSubstring != NULL)
+                    line = ssl_util_ptxtsub(p, oline, ssl_cmd_rewrite_map[i].cpSubstring,
+                                            ssl_cmd_rewrite_map[i].cpSubst);
+                else
+                    line = ssl_cmd_rewrite_map[i].cpSubst;
+                break;
+            }
+            else if (ssl_cmd_rewrite_map[i].fpSubst != NULL) {
+                line = ((char *(*)(pool *, const char *, const char *, const char *))
+                        (ssl_cmd_rewrite_map[i].fpSubst))(p, oline, caCmd, cpArgs);
+                break;
+            }
+        }
+    }
+    if (line != NULL && line[0] != NUL)
+        ap_log_error(APLOG_MARK, APLOG_INFO|APLOG_NOERRNO, s,
+                     "mod_ssl:Compat: MAPPED '%s' => '%s'", oline, line);
+    return line;
+}
+
+/*
+ * The mapping of obsolete environment variables to official ones...
+ */
+
+#define VRM_BEGIN              /* nop */
+#define VRM_ENTRY(var,action)  { var, action },
+#define VRM_END                { NULL, NULL, NULL }
+#define VRM_VAR(old)           old
+#define VRM_SUB(new)           new, NULL
+#define VRM_LOG(msg)           NULL, msg
+
+static struct {
+    char *cpOld;
+    char *cpNew;
+    char *cpMsg;
+} ssl_var_rewrite_map[] = {
+    VRM_BEGIN
+
+    /*
+     * Apache-SSL 1.x, mod_ssl 2.0.x, Sioux 1.x
+     * and Stronghold 2.x backward compatibility
+     */
+    VRM_ENTRY( VRM_VAR("SSL_PROTOCOL_VERSION"),          VRM_SUB("SSL_PROTOCOL")             )
+    VRM_ENTRY( VRM_VAR("SSLEAY_VERSION"),                VRM_SUB("SSL_VERSION_LIBRARY")      )
+    VRM_ENTRY( VRM_VAR("HTTPS_SECRETKEYSIZE"),           VRM_SUB("SSL_CIPHER_USEKEYSIZE")    )
+    VRM_ENTRY( VRM_VAR("HTTPS_KEYSIZE"),                 VRM_SUB("SSL_CIPHER_ALGKEYSIZE")    )
+    VRM_ENTRY( VRM_VAR("HTTPS_CIPHER"),                  VRM_SUB("SSL_CIPHER")               )
+    VRM_ENTRY( VRM_VAR("HTTPS_EXPORT"),                  VRM_SUB("SSL_CIPHER_EXPORT")        )
+    VRM_ENTRY( VRM_VAR("SSL_SERVER_KEY_SIZE"),           VRM_SUB("SSL_CIPHER_ALGKEYSIZE")    )
+    VRM_ENTRY( VRM_VAR("SSL_SERVER_CERTIFICATE"),        VRM_SUB("SSL_SERVER_CERT")          )
+    VRM_ENTRY( VRM_VAR("SSL_SERVER_CERT_START"),         VRM_SUB("SSL_SERVER_V_START")       )
+    VRM_ENTRY( VRM_VAR("SSL_SERVER_CERT_END"),           VRM_SUB("SSL_SERVER_V_END")         )
+    VRM_ENTRY( VRM_VAR("SSL_SERVER_CERT_SERIAL"),        VRM_SUB("SSL_SERVER_M_SERIAL")      )
+    VRM_ENTRY( VRM_VAR("SSL_SERVER_SIGNATURE_ALGORITHM"),VRM_SUB("SSL_SERVER_A_SIG")         )
+    VRM_ENTRY( VRM_VAR("SSL_SERVER_DN"),                 VRM_SUB("SSL_SERVER_S_DN")          )
+    VRM_ENTRY( VRM_VAR("SSL_SERVER_CN"),                 VRM_SUB("SSL_SERVER_S_DN_CN")       )
+    VRM_ENTRY( VRM_VAR("SSL_SERVER_EMAIL"),              VRM_SUB("SSL_SERVER_S_DN_Email")    )
+    VRM_ENTRY( VRM_VAR("SSL_SERVER_O"),                  VRM_SUB("SSL_SERVER_S_DN_O")        )
+    VRM_ENTRY( VRM_VAR("SSL_SERVER_OU"),                 VRM_SUB("SSL_SERVER_S_DN_OU")       )
+    VRM_ENTRY( VRM_VAR("SSL_SERVER_C"),                  VRM_SUB("SSL_SERVER_S_DN_C")        )
+    VRM_ENTRY( VRM_VAR("SSL_SERVER_SP"),                 VRM_SUB("SSL_SERVER_S_DN_SP")       )
+    VRM_ENTRY( VRM_VAR("SSL_SERVER_L"),                  VRM_SUB("SSL_SERVER_S_DN_L")        )
+    VRM_ENTRY( VRM_VAR("SSL_SERVER_IDN"),                VRM_SUB("SSL_SERVER_I_DN")          )
+    VRM_ENTRY( VRM_VAR("SSL_SERVER_ICN"),                VRM_SUB("SSL_SERVER_I_DN_CN")       )
+    VRM_ENTRY( VRM_VAR("SSL_SERVER_IEMAIL"),             VRM_SUB("SSL_SERVER_I_DN_Email")    )
+    VRM_ENTRY( VRM_VAR("SSL_SERVER_IO"),                 VRM_SUB("SSL_SERVER_I_DN_O")        )
+    VRM_ENTRY( VRM_VAR("SSL_SERVER_IOU"),                VRM_SUB("SSL_SERVER_I_DN_OU")       )
+    VRM_ENTRY( VRM_VAR("SSL_SERVER_IC"),                 VRM_SUB("SSL_SERVER_I_DN_C")        )
+    VRM_ENTRY( VRM_VAR("SSL_SERVER_ISP"),                VRM_SUB("SSL_SERVER_I_DN_SP")       )
+    VRM_ENTRY( VRM_VAR("SSL_SERVER_IL"),                 VRM_SUB("SSL_SERVER_I_DN_L")        )
+    VRM_ENTRY( VRM_VAR("SSL_CLIENT_CERTIFICATE"),        VRM_SUB("SSL_CLIENT_CERT")          )
+    VRM_ENTRY( VRM_VAR("SSL_CLIENT_CERT_START"),         VRM_SUB("SSL_CLIENT_V_START")       )
+    VRM_ENTRY( VRM_VAR("SSL_CLIENT_CERT_END"),           VRM_SUB("SSL_CLIENT_V_END")         )
+    VRM_ENTRY( VRM_VAR("SSL_CLIENT_CERT_SERIAL"),        VRM_SUB("SSL_CLIENT_M_SERIAL")      )
+    VRM_ENTRY( VRM_VAR("SSL_CLIENT_SIGNATURE_ALGORITHM"),VRM_SUB("SSL_CLIENT_A_SIG")         )
+    VRM_ENTRY( VRM_VAR("SSL_CLIENT_DN"),                 VRM_SUB("SSL_CLIENT_S_DN")          )
+    VRM_ENTRY( VRM_VAR("SSL_CLIENT_CN"),                 VRM_SUB("SSL_CLIENT_S_DN_CN")       )
+    VRM_ENTRY( VRM_VAR("SSL_CLIENT_EMAIL"),              VRM_SUB("SSL_CLIENT_S_DN_Email")    )
+    VRM_ENTRY( VRM_VAR("SSL_CLIENT_O"),                  VRM_SUB("SSL_CLIENT_S_DN_O")        )
+    VRM_ENTRY( VRM_VAR("SSL_CLIENT_OU"),                 VRM_SUB("SSL_CLIENT_S_DN_OU")       )
+    VRM_ENTRY( VRM_VAR("SSL_CLIENT_C"),                  VRM_SUB("SSL_CLIENT_S_DN_C")        )
+    VRM_ENTRY( VRM_VAR("SSL_CLIENT_SP"),                 VRM_SUB("SSL_CLIENT_S_DN_SP")       )
+    VRM_ENTRY( VRM_VAR("SSL_CLIENT_L"),                  VRM_SUB("SSL_CLIENT_S_DN_L")        )
+    VRM_ENTRY( VRM_VAR("SSL_CLIENT_IDN"),                VRM_SUB("SSL_CLIENT_I_DN")          )
+    VRM_ENTRY( VRM_VAR("SSL_CLIENT_ICN"),                VRM_SUB("SSL_CLIENT_I_DN_CN")       )
+    VRM_ENTRY( VRM_VAR("SSL_CLIENT_IEMAIL"),             VRM_SUB("SSL_CLIENT_I_DN_Email")    )
+    VRM_ENTRY( VRM_VAR("SSL_CLIENT_IO"),                 VRM_SUB("SSL_CLIENT_I_DN_O")        )
+    VRM_ENTRY( VRM_VAR("SSL_CLIENT_IOU"),                VRM_SUB("SSL_CLIENT_I_DN_OU")       )
+    VRM_ENTRY( VRM_VAR("SSL_CLIENT_IC"),                 VRM_SUB("SSL_CLIENT_I_DN_C")        )
+    VRM_ENTRY( VRM_VAR("SSL_CLIENT_ISP"),                VRM_SUB("SSL_CLIENT_I_DN_SP")       )
+    VRM_ENTRY( VRM_VAR("SSL_CLIENT_IL"),                 VRM_SUB("SSL_CLIENT_I_DN_L")        )
+    VRM_ENTRY( VRM_VAR("SSL_EXPORT"),                    VRM_SUB("SSL_CIPHER_EXPORT")        )
+    VRM_ENTRY( VRM_VAR("SSL_KEYSIZE"),                   VRM_SUB("SSL_CIPHER_ALGKEYSIZE")    )
+    VRM_ENTRY( VRM_VAR("SSL_SECRETKEYSIZE"),             VRM_SUB("SSL_CIPHER_USEKEYSIZE")    )
+    VRM_ENTRY( VRM_VAR("SSL_SSLEAY_VERSION"),            VRM_SUB("SSL_VERSION_LIBRARY")      )
+
+    VRM_ENTRY( VRM_VAR("SSL_STRONG_CRYPTO"),             VRM_LOG("Not supported by mod_ssl") )
+    VRM_ENTRY( VRM_VAR("SSL_SERVER_KEY_EXP"),            VRM_LOG("Not supported by mod_ssl") )
+    VRM_ENTRY( VRM_VAR("SSL_SERVER_KEY_SIZE"),           VRM_LOG("Not supported by mod_ssl") )
+    VRM_ENTRY( VRM_VAR("SSL_SERVER_KEY_ALGORITHM"),      VRM_LOG("Not supported by mod_ssl") )
+    VRM_ENTRY( VRM_VAR("SSL_SERVER_SESSIONDIR"),         VRM_LOG("Not supported by mod_ssl") )
+    VRM_ENTRY( VRM_VAR("SSL_SERVER_CERTIFICATELOGDIR"),  VRM_LOG("Not supported by mod_ssl") )
+    VRM_ENTRY( VRM_VAR("SSL_SERVER_CERTFILE"),           VRM_LOG("Not supported by mod_ssl") )
+    VRM_ENTRY( VRM_VAR("SSL_SERVER_KEYFILE"),            VRM_LOG("Not supported by mod_ssl") )
+    VRM_ENTRY( VRM_VAR("SSL_SERVER_KEYFILETYPE"),        VRM_LOG("Not supported by mod_ssl") )
+    VRM_ENTRY( VRM_VAR("SSL_CLIENT_KEY_EXP"),            VRM_LOG("Not supported by mod_ssl") )
+    VRM_ENTRY( VRM_VAR("SSL_CLIENT_KEY_ALGORITHM"),      VRM_LOG("Not supported by mod_ssl") )
+    VRM_ENTRY( VRM_VAR("SSL_CLIENT_KEY_SIZE"),           VRM_LOG("Not supported by mod_ssl") )
+
+    VRM_END
+};
+
+void ssl_compat_variables(request_rec *r)
+{
+    char *cpOld;
+    char *cpNew;
+    char *cpMsg;
+    char *cpVal;
+    int i;
+
+    for (i = 0; ssl_var_rewrite_map[i].cpOld != NULL; i++) {
+        cpOld = ssl_var_rewrite_map[i].cpOld;
+        cpMsg = ssl_var_rewrite_map[i].cpMsg;
+        cpNew = ssl_var_rewrite_map[i].cpNew;
+        if (cpNew != NULL) {
+            cpVal = ssl_var_lookup(r->pool, r->server, r->connection, r, cpNew);
+            if (!strIsEmpty(cpVal))
+                ap_table_set(r->subprocess_env, cpOld, cpVal);
+        }
+        else if (cpMsg != NULL) {
+#ifdef SSL_VENDOR
+           /*
+            * something that isn't provided by mod_ssl, so at least
+            * let vendor extensions provide a reasonable value first.
+            */
+            cpVal = NULL;
+            ap_hook_use("ap::mod_ssl::vendor::compat_variables_lookup",
+                        AP_HOOK_SIG3(ptr,ptr,ptr),
+                        AP_HOOK_DECLINE(NULL),
+                        &cpVal, r, cpOld);
+            if (cpVal != NULL) {
+                ap_table_set(r->subprocess_env, cpOld, cpVal);
+                continue;
+            }
+#endif
+
+            /*
+             * we cannot print a message, so we set at least
+             * the variables content to the compat message
+             */
+            ap_table_set(r->subprocess_env, cpOld, cpMsg);
+        }
+    }
+    return;
+}
+
+#endif /* SSL_COMPAT */
diff --git a/modules/ssl/ssl_engine_config.c b/modules/ssl/ssl_engine_config.c
new file mode 100644 (file)
index 0000000..c47340b
--- /dev/null
@@ -0,0 +1,1093 @@
+/*                      _             _
+**  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
+** | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
+** | | | | | | (_) | (_| |   \__ \__ \ |  www.modssl.org
+** |_| |_| |_|\___/ \__,_|___|___/___/_|  ftp.modssl.org
+**                      |_____|
+**  ssl_engine_config.c
+**  Apache Configuration Directives
+*/
+
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ *    nor may "mod_ssl" appear in their names without prior
+ *    written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+                                      /* ``Damned if you do,
+                                           damned if you don't.''
+                                               -- Unknown        */
+#include "mod_ssl.h"
+
+
+/*  _________________________________________________________________
+**
+**  Support for Global Configuration
+**  _________________________________________________________________
+*/
+
+void ssl_hook_AddModule(module *m)
+{
+    if (m == &ssl_module) {
+        /*
+         * Announce us for the configuration files
+         */
+        ap_add_config_define("MOD_SSL");
+
+        /*
+         * Link ourself into the Apache kernel
+         */
+        ssl_var_register();
+        ssl_ext_register();
+        ssl_io_register();
+#if defined(SSL_VENDOR) && defined(SSL_VENDOR_OBJS)
+        ssl_vendor_register();
+#endif
+    }
+    return;
+}
+
+void ssl_hook_RemoveModule(module *m)
+{
+    if (m == &ssl_module) {
+        /*
+         * Unlink ourself from the Apache kernel
+         */
+        ssl_var_unregister();
+        ssl_ext_unregister();
+        ssl_io_unregister();
+#if defined(SSL_VENDOR) && defined(SSL_VENDOR_OBJS)
+        ssl_vendor_unregister();
+#endif
+    }
+    return;
+}
+
+void ssl_config_global_create(void)
+{
+    pool *pPool;
+    SSLModConfigRec *mc;
+
+    mc = ap_ctx_get(ap_global_ctx, "ssl_module");
+    if (mc == NULL) {
+        /*
+         * allocate an own subpool which survives server restarts
+         */
+        pPool = ap_make_sub_pool(NULL);
+        mc = (SSLModConfigRec *)ap_palloc(pPool, sizeof(SSLModConfigRec));
+        mc->pPool = pPool;
+        mc->bFixed = FALSE;
+
+        /*
+         * initialize per-module configuration
+         */
+        mc->nInitCount             = 0;
+        mc->nSessionCacheMode      = SSL_SCMODE_UNSET;
+        mc->szSessionCacheDataFile = NULL;
+        mc->nSessionCacheDataSize  = 0;
+        mc->pSessionCacheDataMM    = NULL;
+        mc->tSessionCacheDataTable = NULL;
+        mc->nMutexMode             = SSL_MUTEXMODE_UNSET;
+        mc->szMutexFile            = NULL;
+        mc->nMutexFD               = -1;
+        mc->nMutexSEMID            = -1;
+        mc->aRandSeed              = ap_make_array(pPool, 4, sizeof(ssl_randseed_t));
+        mc->tPrivateKey            = ssl_ds_table_make(pPool, sizeof(ssl_asn1_t));
+        mc->tPublicCert            = ssl_ds_table_make(pPool, sizeof(ssl_asn1_t));
+        mc->tTmpKeys               = ssl_ds_table_make(pPool, sizeof(ssl_asn1_t));
+#ifdef SSL_EXPERIMENTAL_ENGINE
+        mc->szCryptoDevice         = NULL;
+#endif
+
+        (void)memset(mc->pTmpKeys, 0, SSL_TKPIDX_MAX*sizeof(void *));
+
+#ifdef SSL_VENDOR
+        mc->ctx = ap_ctx_new(pPool);
+        ap_hook_use("ap::mod_ssl::vendor::config_global_create",
+                AP_HOOK_SIG2(void,ptr), AP_HOOK_MODE_ALL, mc);
+#endif
+
+        /*
+         * And push it into Apache's global context
+         */
+        ap_ctx_set(ap_global_ctx, "ssl_module", mc);
+    }
+    return;
+}
+
+void ssl_config_global_fix(void)
+{
+    SSLModConfigRec *mc = myModConfig();
+    mc->bFixed = TRUE;
+    return;
+}
+
+BOOL ssl_config_global_isfixed(void)
+{
+    SSLModConfigRec *mc = myModConfig();
+    return (mc->bFixed);
+}
+
+
+/*  _________________________________________________________________
+**
+**  Configuration handling
+**  _________________________________________________________________
+*/
+
+/*
+ *  Create per-server SSL configuration
+ */
+void *ssl_config_server_create(pool *p, server_rec *s)
+{
+    SSLSrvConfigRec *sc;
+
+    ssl_config_global_create();
+
+    sc = ap_palloc(p, sizeof(SSLSrvConfigRec));
+    sc->bEnabled               = UNSET;
+    sc->szCACertificatePath    = NULL;
+    sc->szCACertificateFile    = NULL;
+    sc->szCertificateChain     = NULL;
+    sc->szLogFile              = NULL;
+    sc->szCipherSuite          = NULL;
+    sc->nLogLevel              = SSL_LOG_NONE;
+    sc->nVerifyDepth           = UNSET;
+    sc->nVerifyClient          = SSL_CVERIFY_UNSET;
+    sc->nSessionCacheTimeout   = UNSET;
+    sc->nPassPhraseDialogType  = SSL_PPTYPE_UNSET;
+    sc->szPassPhraseDialogPath = NULL;
+    sc->nProtocol              = SSL_PROTOCOL_ALL;
+    sc->fileLogFile            = NULL;
+    sc->pSSLCtx                = NULL;
+    sc->szCARevocationPath     = NULL;
+    sc->szCARevocationFile     = NULL;
+    sc->pRevocationStore       = NULL;
+
+#ifdef SSL_EXPERIMENTAL_PROXY
+    sc->nProxyVerifyDepth             = UNSET;
+    sc->szProxyCACertificatePath      = NULL;
+    sc->szProxyCACertificateFile      = NULL;
+    sc->szProxyClientCertificateFile  = NULL;
+    sc->szProxyClientCertificatePath  = NULL;
+    sc->szProxyCipherSuite            = NULL;
+    sc->nProxyProtocol                = SSL_PROTOCOL_ALL & ~SSL_PROTOCOL_TLSV1;
+    sc->bProxyVerify                  = UNSET;
+    sc->pSSLProxyCtx                  = NULL;
+#endif
+
+    (void)memset(sc->szPublicCertFile, 0, SSL_AIDX_MAX*sizeof(char *));
+    (void)memset(sc->szPrivateKeyFile, 0, SSL_AIDX_MAX*sizeof(char *));
+    (void)memset(sc->pPublicCert, 0, SSL_AIDX_MAX*sizeof(X509 *));
+    (void)memset(sc->pPrivateKey, 0, SSL_AIDX_MAX*sizeof(EVP_PKEY *));
+
+#ifdef SSL_VENDOR
+    sc->ctx = ap_ctx_new(p);
+    ap_hook_use("ap::mod_ssl::vendor::config_server_create",
+                AP_HOOK_SIG4(void,ptr,ptr,ptr), AP_HOOK_MODE_ALL,
+                p, s, sc);
+#endif
+
+    return sc;
+}
+
+/*
+ *  Merge per-server SSL configurations
+ */
+void *ssl_config_server_merge(pool *p, void *basev, void *addv)
+{
+    SSLSrvConfigRec *base = (SSLSrvConfigRec *)basev;
+    SSLSrvConfigRec *add  = (SSLSrvConfigRec *)addv;
+    SSLSrvConfigRec *new  = (SSLSrvConfigRec *)ap_palloc(p, sizeof(SSLSrvConfigRec));
+    int i;
+
+    cfgMergeBool(bEnabled);
+    cfgMergeString(szCACertificatePath);
+    cfgMergeString(szCACertificateFile);
+    cfgMergeString(szCertificateChain);
+    cfgMergeString(szLogFile);
+    cfgMergeString(szCipherSuite);
+    cfgMerge(nLogLevel, SSL_LOG_NONE);
+    cfgMergeInt(nVerifyDepth);
+    cfgMerge(nVerifyClient, SSL_CVERIFY_UNSET);
+    cfgMergeInt(nSessionCacheTimeout);
+    cfgMerge(nPassPhraseDialogType, SSL_PPTYPE_UNSET);
+    cfgMergeString(szPassPhraseDialogPath);
+    cfgMerge(nProtocol, SSL_PROTOCOL_ALL);
+    cfgMerge(fileLogFile, NULL);
+    cfgMerge(pSSLCtx, NULL);
+    cfgMerge(szCARevocationPath, NULL);
+    cfgMerge(szCARevocationFile, NULL);
+    cfgMerge(pRevocationStore, NULL);
+
+    for (i = 0; i < SSL_AIDX_MAX; i++) {
+        cfgMergeString(szPublicCertFile[i]);
+        cfgMergeString(szPrivateKeyFile[i]);
+        cfgMerge(pPublicCert[i], NULL);
+        cfgMerge(pPrivateKey[i], NULL);
+    }
+
+#ifdef SSL_VENDOR
+    cfgMergeCtx(ctx);
+    ap_hook_use("ap::mod_ssl::vendor::config_server_merge",
+                AP_HOOK_SIG5(void,ptr,ptr,ptr,ptr), AP_HOOK_MODE_ALL,
+                p, base, add, new);
+#endif
+
+#ifdef SSL_EXPERIMENTAL_PROXY
+    cfgMergeInt(nProxyVerifyDepth);
+    cfgMergeString(szProxyCACertificatePath);
+    cfgMergeString(szProxyCACertificateFile);
+    cfgMergeString(szProxyClientCertificateFile);
+    cfgMergeString(szProxyClientCertificatePath);
+    cfgMergeString(szProxyCipherSuite);
+    cfgMerge(nProxyProtocol, (SSL_PROTOCOL_ALL & ~SSL_PROTOCOL_TLSV1));
+    cfgMergeBool(bProxyVerify);
+    cfgMerge(pSSLProxyCtx, NULL);
+#endif
+
+    return new;
+}
+
+/*
+ *  Create per-directory SSL configuration
+ */
+void *ssl_config_perdir_create(pool *p, char *dir)
+{
+    SSLDirConfigRec *dc = ap_palloc(p, sizeof(SSLDirConfigRec));
+
+    dc->bSSLRequired  = FALSE;
+    dc->aRequirement  = ap_make_array(p, 4, sizeof(ssl_require_t));
+    dc->nOptions      = SSL_OPT_NONE|SSL_OPT_RELSET;
+    dc->nOptionsAdd   = SSL_OPT_NONE;
+    dc->nOptionsDel   = SSL_OPT_NONE;
+
+    dc->szCipherSuite          = NULL;
+    dc->nVerifyClient          = SSL_CVERIFY_UNSET;
+    dc->nVerifyDepth           = UNSET;
+#ifdef SSL_EXPERIMENTAL_PERDIRCA
+    dc->szCACertificatePath    = NULL;
+    dc->szCACertificateFile    = NULL;
+#endif
+
+#ifdef SSL_VENDOR
+    dc->ctx = ap_ctx_new(p);
+    ap_hook_use("ap::mod_ssl::vendor::config_perdir_create",
+                AP_HOOK_SIG4(void,ptr,ptr,ptr), AP_HOOK_MODE_ALL,
+                p, dir, dc);
+#endif
+
+    return dc;
+}
+
+/*
+ *  Merge per-directory SSL configurations
+ */
+void *ssl_config_perdir_merge(pool *p, void *basev, void *addv)
+{
+    SSLDirConfigRec *base = (SSLDirConfigRec *)basev;
+    SSLDirConfigRec *add  = (SSLDirConfigRec *)addv;
+    SSLDirConfigRec *new  = (SSLDirConfigRec *)ap_palloc(p,
+                                               sizeof(SSLDirConfigRec));
+
+    cfgMerge(bSSLRequired, FALSE);
+    cfgMergeArray(aRequirement);
+
+    if (add->nOptions & SSL_OPT_RELSET) {
+        new->nOptionsAdd = (base->nOptionsAdd & ~(add->nOptionsDel)) | add->nOptionsAdd;
+        new->nOptionsDel = (base->nOptionsDel & ~(add->nOptionsAdd)) | add->nOptionsDel;
+        new->nOptions    = (base->nOptions    & ~(new->nOptionsDel)) | new->nOptionsAdd;
+    }
+    else {
+        new->nOptions    = add->nOptions;
+        new->nOptionsAdd = add->nOptionsAdd;
+        new->nOptionsDel = add->nOptionsDel;
+    }
+
+    cfgMergeString(szCipherSuite);
+    cfgMerge(nVerifyClient, SSL_CVERIFY_UNSET);
+    cfgMergeInt(nVerifyDepth);
+#ifdef SSL_EXPERIMENTAL_PERDIRCA
+    cfgMergeString(szCACertificatePath);
+    cfgMergeString(szCACertificateFile);
+#endif
+
+#ifdef SSL_VENDOR
+    cfgMergeCtx(ctx);
+    ap_hook_use("ap::mod_ssl::vendor::config_perdir_merge",
+                AP_HOOK_SIG5(void,ptr,ptr,ptr,ptr), AP_HOOK_MODE_ALL,
+                p, base, add, new);
+#endif
+
+    return new;
+}
+
+/*
+ * Directive Rewriting
+ */
+
+char *ssl_hook_RewriteCommand(cmd_parms *cmd, void *config, const char *cmd_line)
+{
+#ifdef SSL_COMPAT
+    return ssl_compat_directive(cmd->server, cmd->pool, cmd_line);
+#else
+    return NULL;
+#endif
+}
+
+/*
+ *  Configuration functions for particular directives
+ */
+
+const char *ssl_cmd_SSLMutex(
+    cmd_parms *cmd, char *struct_ptr, char *arg)
+{
+    const char *err;
+    SSLModConfigRec *mc = myModConfig();
+
+    if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY)) != NULL)
+        return err;
+    if (ssl_config_global_isfixed())
+        return NULL;
+    if (strcEQ(arg, "none")) {
+        mc->nMutexMode  = SSL_MUTEXMODE_NONE;
+    }
+    else if (strlen(arg) > 5 && strcEQn(arg, "file:", 5)) {
+#ifndef WIN32
+        mc->nMutexMode  = SSL_MUTEXMODE_FILE;
+        mc->szMutexFile = ap_psprintf(mc->pPool, "%s.%lu",
+                                      ssl_util_server_root_relative(cmd->pool, "mutex", arg+5),
+                                      (unsigned long)getpid());
+#else
+        return "SSLMutex: Lockfiles not available on this platform";
+#endif
+    }
+    else if (strcEQ(arg, "sem")) {
+#ifdef SSL_CAN_USE_SEM
+        mc->nMutexMode  = SSL_MUTEXMODE_SEM;
+#else
+        return "SSLMutex: Semaphores not available on this platform";
+#endif
+    }
+    else
+        return "SSLMutex: Invalid argument";
+    return NULL;
+}
+
+const char *ssl_cmd_SSLPassPhraseDialog(
+    cmd_parms *cmd, char *struct_ptr, char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    const char *err;
+
+    if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY)) != NULL)
+        return err;
+    if (strcEQ(arg, "builtin")) {
+        sc->nPassPhraseDialogType  = SSL_PPTYPE_BUILTIN;
+        sc->szPassPhraseDialogPath = NULL;
+    }
+    else if (strlen(arg) > 5 && strEQn(arg, "exec:", 5)) {
+        sc->nPassPhraseDialogType  = SSL_PPTYPE_FILTER;
+        sc->szPassPhraseDialogPath = ssl_util_server_root_relative(cmd->pool, "dialog", arg+5);
+        if (!ssl_util_path_check(SSL_PCM_EXISTS, sc->szPassPhraseDialogPath))
+            return ap_pstrcat(cmd->pool, "SSLPassPhraseDialog: file '",
+                              sc->szPassPhraseDialogPath, "' not exists", NULL);
+    }
+    else
+        return "SSLPassPhraseDialog: Invalid argument";
+    return NULL;
+}
+
+#ifdef SSL_EXPERIMENTAL_ENGINE
+const char *ssl_cmd_SSLCryptoDevice(
+    cmd_parms *cmd, char *struct_ptr, char *arg)
+{
+    SSLModConfigRec *mc = myModConfig();
+    const char *err;
+    ENGINE *e;
+#if SSL_LIBRARY_VERSION >= 0x00907000
+    static int loaded_engines = FALSE;
+
+    /* early loading to make sure the engines are already 
+       available for ENGINE_by_id() above... */
+    if (!loaded_engines) {
+        ENGINE_load_builtin_engines();
+        loaded_engines = TRUE;
+    }
+#endif
+    if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY)) != NULL)
+        return err;
+    if (strcEQ(arg, "builtin")) {
+        mc->szCryptoDevice = NULL;
+    }
+    else if ((e = ENGINE_by_id(arg)) != NULL) {
+        mc->szCryptoDevice = arg;
+        ENGINE_free(e);
+    }
+    else
+        return "SSLCryptoDevice: Invalid argument";
+    return NULL;
+}
+#endif
+
+const char *ssl_cmd_SSLRandomSeed(
+    cmd_parms *cmd, char *struct_ptr, char *arg1, char *arg2, char *arg3)
+{
+    SSLModConfigRec *mc = myModConfig();
+    const char *err;
+    ssl_randseed_t *pRS;
+
+    if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY)) != NULL)
+        return err;
+    if (ssl_config_global_isfixed())
+        return NULL;
+    pRS = ap_push_array(mc->aRandSeed);
+    if (strcEQ(arg1, "startup"))
+        pRS->nCtx = SSL_RSCTX_STARTUP;
+    else if (strcEQ(arg1, "connect"))
+        pRS->nCtx = SSL_RSCTX_CONNECT;
+    else
+        return ap_pstrcat(cmd->pool, "SSLRandomSeed: "
+                          "invalid context: `", arg1, "'");
+    if (strlen(arg2) > 5 && strEQn(arg2, "file:", 5)) {
+        pRS->nSrc   = SSL_RSSRC_FILE;
+        pRS->cpPath = ap_pstrdup(mc->pPool, ssl_util_server_root_relative(cmd->pool, "random", arg2+5));
+    }
+    else if (strlen(arg2) > 5 && strEQn(arg2, "exec:", 5)) {
+        pRS->nSrc   = SSL_RSSRC_EXEC;
+        pRS->cpPath = ap_pstrdup(mc->pPool, ssl_util_server_root_relative(cmd->pool, "random", arg2+5));
+    }
+#if SSL_LIBRARY_VERSION >= 0x00905100
+    else if (strlen(arg2) > 4 && strEQn(arg2, "egd:", 4)) {
+        pRS->nSrc   = SSL_RSSRC_EGD;
+        pRS->cpPath = ap_pstrdup(mc->pPool, ssl_util_server_root_relative(cmd->pool, "random", arg2+4));
+    }
+#endif
+    else if (strcEQ(arg2, "builtin")) {
+        pRS->nSrc   = SSL_RSSRC_BUILTIN;
+        pRS->cpPath = NULL;
+    }
+    else {
+        pRS->nSrc   = SSL_RSSRC_FILE;
+        pRS->cpPath = ap_pstrdup(mc->pPool, ssl_util_server_root_relative(cmd->pool, "random", arg2));
+    }
+    if (pRS->nSrc != SSL_RSSRC_BUILTIN)
+        if (!ssl_util_path_check(SSL_PCM_EXISTS, pRS->cpPath))
+            return ap_pstrcat(cmd->pool, "SSLRandomSeed: source path '",
+                              pRS->cpPath, "' not exists", NULL);
+    if (arg3 == NULL)
+        pRS->nBytes = 0; /* read whole file */
+    else {
+        if (pRS->nSrc == SSL_RSSRC_BUILTIN)
+            return "SSLRandomSeed: byte specification not "
+                   "allowed for builtin seed source";
+        pRS->nBytes = atoi(arg3);
+        if (pRS->nBytes < 0)
+            return "SSLRandomSeed: invalid number of bytes specified";
+    }
+    return NULL;
+}
+
+const char *ssl_cmd_SSLEngine(
+    cmd_parms *cmd, char *struct_ptr, int flag)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+
+    sc->bEnabled = (flag ? TRUE : FALSE);
+    return NULL;
+}
+
+const char *ssl_cmd_SSLCipherSuite(
+    cmd_parms *cmd, SSLDirConfigRec *dc, char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+
+    if (cmd->path == NULL || dc == NULL)
+        sc->szCipherSuite = arg;
+    else
+        dc->szCipherSuite = arg;
+    return NULL;
+}
+
+const char *ssl_cmd_SSLCertificateFile(
+    cmd_parms *cmd, char *struct_ptr, char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    char *cpPath;
+    int i;
+
+    cpPath = ssl_util_server_root_relative(cmd->pool, "certkey", arg);
+    if (!ssl_util_path_check(SSL_PCM_EXISTS|SSL_PCM_ISREG|SSL_PCM_ISNONZERO, cpPath))
+        return ap_pstrcat(cmd->pool, "SSLCertificateFile: file '",
+                          cpPath, "' not exists or empty", NULL);
+    for (i = 0; i < SSL_AIDX_MAX && sc->szPublicCertFile[i] != NULL; i++)
+        ;
+    if (i == SSL_AIDX_MAX)
+        return ap_psprintf(cmd->pool, "SSLCertificateFile: only up to %d "
+                          "different certificates per virtual host allowed", 
+                          SSL_AIDX_MAX);
+    sc->szPublicCertFile[i] = cpPath;
+    return NULL;
+}
+
+const char *ssl_cmd_SSLCertificateKeyFile(
+    cmd_parms *cmd, char *struct_ptr, char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    char *cpPath;
+    int i;
+
+    cpPath = ssl_util_server_root_relative(cmd->pool, "certkey", arg);
+    if (!ssl_util_path_check(SSL_PCM_EXISTS|SSL_PCM_ISREG|SSL_PCM_ISNONZERO, cpPath))
+        return ap_pstrcat(cmd->pool, "SSLCertificateKeyFile: file '",
+                          cpPath, "' not exists or empty", NULL);
+    for (i = 0; i < SSL_AIDX_MAX && sc->szPrivateKeyFile[i] != NULL; i++)
+        ;
+    if (i == SSL_AIDX_MAX)
+        return ap_psprintf(cmd->pool, "SSLCertificateKeyFile: only up to %d "
+                          "different private keys per virtual host allowed", 
+                          SSL_AIDX_MAX);
+    sc->szPrivateKeyFile[i] = cpPath;
+    return NULL;
+}
+
+const char *ssl_cmd_SSLCertificateChainFile(
+    cmd_parms *cmd, char *struct_ptr, char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    char *cpPath;
+
+    cpPath = ssl_util_server_root_relative(cmd->pool, "certkey", arg);
+    if (!ssl_util_path_check(SSL_PCM_EXISTS|SSL_PCM_ISREG|SSL_PCM_ISNONZERO, cpPath))
+        return ap_pstrcat(cmd->pool, "SSLCertificateChainFile: file '",
+                          cpPath, "' not exists or empty", NULL);
+    sc->szCertificateChain = cpPath;
+    return NULL;
+}
+
+const char *ssl_cmd_SSLCACertificatePath(
+    cmd_parms *cmd, SSLDirConfigRec *dc, char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    char *cpPath;
+
+    cpPath = ssl_util_server_root_relative(cmd->pool, "certkey", arg);
+    if (!ssl_util_path_check(SSL_PCM_EXISTS|SSL_PCM_ISDIR, cpPath))
+        return ap_pstrcat(cmd->pool, "SSLCACertificatePath: directory '",
+                          cpPath, "' not exists", NULL);
+#ifdef SSL_EXPERIMENTAL_PERDIRCA
+    if (cmd->path == NULL || dc == NULL)
+        sc->szCACertificatePath = cpPath;
+    else
+        dc->szCACertificatePath = cpPath;
+#else
+    sc->szCACertificatePath = cpPath;
+#endif
+    return NULL;
+}
+
+const char *ssl_cmd_SSLCACertificateFile(
+    cmd_parms *cmd, SSLDirConfigRec *dc, char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    char *cpPath;
+
+    cpPath = ssl_util_server_root_relative(cmd->pool, "certkey", arg);
+    if (!ssl_util_path_check(SSL_PCM_EXISTS|SSL_PCM_ISREG|SSL_PCM_ISNONZERO, cpPath))
+        return ap_pstrcat(cmd->pool, "SSLCACertificateFile: file '",
+                          cpPath, "' not exists or empty", NULL);
+#ifdef SSL_EXPERIMENTAL_PERDIRCA
+    if (cmd->path == NULL || dc == NULL)
+        sc->szCACertificateFile = cpPath;
+    else
+        dc->szCACertificateFile = cpPath;
+#else
+    sc->szCACertificateFile = cpPath;
+#endif
+    return NULL;
+}
+
+const char *ssl_cmd_SSLCARevocationPath(
+    cmd_parms *cmd, SSLDirConfigRec *dc, char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    char *cpPath;
+
+    cpPath = ssl_util_server_root_relative(cmd->pool, "certkey", arg);
+    if (!ssl_util_path_check(SSL_PCM_EXISTS|SSL_PCM_ISDIR, cpPath))
+        return ap_pstrcat(cmd->pool, "SSLCARecocationPath: directory '",
+                          cpPath, "' not exists", NULL);
+    sc->szCARevocationPath = cpPath;
+    return NULL;
+}
+
+const char *ssl_cmd_SSLCARevocationFile(
+    cmd_parms *cmd, SSLDirConfigRec *dc, char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    char *cpPath;
+
+    cpPath = ssl_util_server_root_relative(cmd->pool, "certkey", arg);
+    if (!ssl_util_path_check(SSL_PCM_EXISTS|SSL_PCM_ISREG|SSL_PCM_ISNONZERO, cpPath))
+        return ap_pstrcat(cmd->pool, "SSLCARevocationFile: file '",
+                          cpPath, "' not exists or empty", NULL);
+    sc->szCARevocationFile = cpPath;
+    return NULL;
+}
+
+const char *ssl_cmd_SSLVerifyClient(
+    cmd_parms *cmd, SSLDirConfigRec *dc, char *level)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    ssl_verify_t id;
+
+    if (strEQ(level, "0") || strcEQ(level, "none"))
+        id = SSL_CVERIFY_NONE;
+    else if (strEQ(level, "1") || strcEQ(level, "optional"))
+        id = SSL_CVERIFY_OPTIONAL;
+    else if (strEQ(level, "2") || strcEQ(level, "require"))
+        id = SSL_CVERIFY_REQUIRE;
+    else if (strEQ(level, "3") || strcEQ(level, "optional_no_ca"))
+        id = SSL_CVERIFY_OPTIONAL_NO_CA;
+    else
+        return "SSLVerifyClient: Invalid argument";
+    if (cmd->path == NULL || dc == NULL)
+        sc->nVerifyClient = id;
+    else
+        dc->nVerifyClient = id;
+    return NULL;
+}
+
+const char *ssl_cmd_SSLVerifyDepth(
+    cmd_parms *cmd, SSLDirConfigRec *dc, char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    int d;
+
+    d = atoi(arg);
+    if (d < 0)
+        return "SSLVerifyDepth: Invalid argument";
+    if (cmd->path == NULL || dc == NULL)
+        sc->nVerifyDepth = d;
+    else
+        dc->nVerifyDepth = d;
+    return NULL;
+}
+
+const char *ssl_cmd_SSLSessionCache(
+    cmd_parms *cmd, char *struct_ptr, char *arg)
+{
+    const char *err;
+    SSLModConfigRec *mc = myModConfig();
+    char *cp, *cp2;
+    int maxsize;
+
+    if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY)) != NULL)
+        return err;
+    if (ssl_config_global_isfixed())
+        return NULL;
+    if (strcEQ(arg, "none")) {
+        mc->nSessionCacheMode      = SSL_SCMODE_NONE;
+        mc->szSessionCacheDataFile = NULL;
+    }
+    else if (strlen(arg) > 4 && strcEQn(arg, "dbm:", 4)) {
+        mc->nSessionCacheMode      = SSL_SCMODE_DBM;
+        mc->szSessionCacheDataFile = ap_pstrdup(mc->pPool,
+                                     ssl_util_server_root_relative(cmd->pool, "scache", arg+4));
+    }
+    else if (   (strlen(arg) > 4 && strcEQn(arg, "shm:",   4)) 
+             || (strlen(arg) > 6 && strcEQn(arg, "shmht:", 6))) {
+        if (!ap_mm_useable())
+            return "SSLSessionCache: shared memory cache not useable on this platform";
+        mc->nSessionCacheMode      = SSL_SCMODE_SHMHT;
+        cp = strchr(arg, ':');
+        mc->szSessionCacheDataFile = ap_pstrdup(mc->pPool,
+                                     ssl_util_server_root_relative(cmd->pool, "scache", cp+1));
+        mc->tSessionCacheDataTable = NULL;
+        mc->nSessionCacheDataSize  = 1024*512; /* 512KB */
+        if ((cp = strchr(mc->szSessionCacheDataFile, '(')) != NULL) {
+            *cp++ = NUL;
+            if ((cp2 = strchr(cp, ')')) == NULL)
+                return "SSLSessionCache: Invalid argument: no closing parenthesis";
+            *cp2 = NUL;
+            mc->nSessionCacheDataSize = atoi(cp);
+            if (mc->nSessionCacheDataSize <= 8192)
+                return "SSLSessionCache: Invalid argument: size has to be >= 8192 bytes";
+            maxsize = ap_mm_core_maxsegsize();
+            if (mc->nSessionCacheDataSize >= maxsize)
+                return ap_psprintf(cmd->pool, "SSLSessionCache: Invalid argument: "
+                                   "size has to be < %d bytes on this platform", maxsize);
+        }
+    }
+    else if (strlen(arg) > 6 && strcEQn(arg, "shmcb:", 6)) {
+        if (!ap_mm_useable())
+            return "SSLSessionCache: shared memory cache not useable on this platform";
+        mc->nSessionCacheMode      = SSL_SCMODE_SHMCB;
+        mc->szSessionCacheDataFile = ap_pstrdup(mc->pPool,
+                                     ap_server_root_relative(cmd->pool, arg+6));
+        mc->tSessionCacheDataTable = NULL;
+        mc->nSessionCacheDataSize  = 1024*512; /* 512KB */
+        if ((cp = strchr(mc->szSessionCacheDataFile, '(')) != NULL) {
+            *cp++ = NUL;
+            if ((cp2 = strchr(cp, ')')) == NULL)
+                return "SSLSessionCache: Invalid argument: no closing parenthesis";
+            *cp2 = NUL;
+            mc->nSessionCacheDataSize = atoi(cp);
+            if (mc->nSessionCacheDataSize <= 8192)
+                return "SSLSessionCache: Invalid argument: size has to be >= 8192 bytes";
+            maxsize = ap_mm_core_maxsegsize();
+            if (mc->nSessionCacheDataSize >= maxsize)
+                return ap_psprintf(cmd->pool, "SSLSessionCache: Invalid argument: "
+                                   "size has to be < %d bytes on this platform", maxsize);
+        }
+    }
+       else
+#ifdef SSL_VENDOR
+        if (!ap_hook_use("ap::mod_ssl::vendor::cmd_sslsessioncache",
+             AP_HOOK_SIG4(void,ptr,ptr,ptr), AP_HOOK_MODE_ALL,
+             cmd, arg, mc))
+#endif
+        return "SSLSessionCache: Invalid argument";
+    return NULL;
+}
+
+const char *ssl_cmd_SSLSessionCacheTimeout(
+    cmd_parms *cmd, char *struct_ptr, char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+
+    sc->nSessionCacheTimeout = atoi(arg);
+    if (sc->nSessionCacheTimeout < 0)
+        return "SSLSessionCacheTimeout: Invalid argument";
+    return NULL;
+}
+
+const char *ssl_cmd_SSLLog(
+    cmd_parms *cmd, char *struct_ptr, char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    const char *err;
+
+    if ((err = ap_check_cmd_context(cmd,  NOT_IN_LIMIT|NOT_IN_DIRECTORY
+                                         |NOT_IN_LOCATION|NOT_IN_FILES )) != NULL)
+        return err;
+    sc->szLogFile = arg;
+    return NULL;
+}
+
+const char *ssl_cmd_SSLLogLevel(
+    cmd_parms *cmd, char *struct_ptr, char *level)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    const char *err;
+
+    if ((err = ap_check_cmd_context(cmd,  NOT_IN_LIMIT|NOT_IN_DIRECTORY
+                                         |NOT_IN_LOCATION|NOT_IN_FILES )) != NULL)
+        return err;
+    if (strcEQ(level, "none"))
+        sc->nLogLevel = SSL_LOG_NONE;
+    else if (strcEQ(level, "error"))
+        sc->nLogLevel = SSL_LOG_ERROR;
+    else if (strcEQ(level, "warn"))
+        sc->nLogLevel = SSL_LOG_WARN;
+    else if (strcEQ(level, "info"))
+        sc->nLogLevel = SSL_LOG_INFO;
+    else if (strcEQ(level, "trace"))
+        sc->nLogLevel = SSL_LOG_TRACE;
+    else if (strcEQ(level, "debug"))
+        sc->nLogLevel = SSL_LOG_DEBUG;
+    else
+        return "SSLLogLevel: Invalid argument";
+    return NULL;
+}
+
+const char *ssl_cmd_SSLOptions(
+    cmd_parms *cmd, SSLDirConfigRec *dc, const char *cpLine)
+{
+    ssl_opt_t opt;
+    int first;
+    char action;
+    char *w;
+
+    first = TRUE;
+    while (cpLine[0] != NUL) {
+        w = ap_getword_conf(cmd->pool, &cpLine);
+        action = NUL;
+
+        if (*w == '+' || *w == '-') {
+            action = *(w++);
+        }
+        else if (first) {
+            dc->nOptions = SSL_OPT_NONE;
+            first = FALSE;
+        }
+
+        if (strcEQ(w, "StdEnvVars"))
+            opt = SSL_OPT_STDENVVARS;
+        else if (strcEQ(w, "CompatEnvVars"))
+            opt = SSL_OPT_COMPATENVVARS;
+        else if (strcEQ(w, "ExportCertData"))
+            opt = SSL_OPT_EXPORTCERTDATA;
+        else if (strcEQ(w, "FakeBasicAuth"))
+            opt = SSL_OPT_FAKEBASICAUTH;
+        else if (strcEQ(w, "StrictRequire"))
+            opt = SSL_OPT_STRICTREQUIRE;
+        else if (strcEQ(w, "OptRenegotiate"))
+            opt = SSL_OPT_OPTRENEGOTIATE;
+        else
+            return ap_pstrcat(cmd->pool, "SSLOptions: Illegal option '", w, "'", NULL);
+
+        if (action == '-') {
+            dc->nOptionsAdd &= ~opt;
+            dc->nOptionsDel |=  opt;
+            dc->nOptions    &= ~opt;
+        }
+        else if (action == '+') {
+            dc->nOptionsAdd |=  opt;
+            dc->nOptionsDel &= ~opt;
+            dc->nOptions    |=  opt;
+        }
+        else {
+            dc->nOptions    = opt;
+            dc->nOptionsAdd = opt;
+            dc->nOptionsDel = SSL_OPT_NONE;
+        }
+    }
+    return NULL;
+}
+
+const char *ssl_cmd_SSLRequireSSL(
+    cmd_parms *cmd, SSLDirConfigRec *dc, char *cipher)
+{
+    dc->bSSLRequired = TRUE;
+    return NULL;
+}
+
+const char *ssl_cmd_SSLRequire(
+    cmd_parms *cmd, SSLDirConfigRec *dc, char *cpExpr)
+{
+    ssl_expr *mpExpr;
+    ssl_require_t *pReqRec;
+
+    if ((mpExpr = ssl_expr_comp(cmd->pool, cpExpr)) == NULL)
+        return ap_pstrcat(cmd->pool, "SSLRequire: ", ssl_expr_get_error(), NULL);
+    pReqRec = ap_push_array(dc->aRequirement);
+    pReqRec->cpExpr = ap_pstrdup(cmd->pool, cpExpr);
+    pReqRec->mpExpr = mpExpr;
+    return NULL;
+}
+
+const char *ssl_cmd_SSLProtocol(
+    cmd_parms *cmd, char *struct_ptr, const char *opt)
+{
+    SSLSrvConfigRec *sc;
+    ssl_proto_t options, thisopt;
+    char action;
+    char *w;
+
+    sc = mySrvConfig(cmd->server);
+    options = SSL_PROTOCOL_NONE;
+    while (opt[0] != NUL) {
+        w = ap_getword_conf(cmd->pool, &opt);
+
+        action = NUL;
+        if (*w == '+' || *w == '-')
+            action = *(w++);
+
+        if (strcEQ(w, "SSLv2"))
+            thisopt = SSL_PROTOCOL_SSLV2;
+        else if (strcEQ(w, "SSLv3"))
+            thisopt = SSL_PROTOCOL_SSLV3;
+        else if (strcEQ(w, "TLSv1"))
+            thisopt = SSL_PROTOCOL_TLSV1;
+        else if (strcEQ(w, "all"))
+            thisopt = SSL_PROTOCOL_ALL;
+        else
+            return ap_pstrcat(cmd->pool, "SSLProtocol: Illegal protocol '", w, "'", NULL);
+
+        if (action == '-')
+            options &= ~thisopt;
+        else if (action == '+')
+            options |= thisopt;
+        else
+            options = thisopt;
+    }
+    sc->nProtocol = options;
+    return NULL;
+}
+
+#ifdef SSL_EXPERIMENTAL_PROXY
+
+const char *ssl_cmd_SSLProxyProtocol(
+    cmd_parms *cmd, char *struct_ptr, const char *opt)
+{
+    SSLSrvConfigRec *sc;
+    ssl_proto_t options, thisopt;
+    char action;
+    char *w;
+
+    sc = mySrvConfig(cmd->server);
+    options = SSL_PROTOCOL_NONE;
+    while (opt[0] != NUL) {
+        w = ap_getword_conf(cmd->pool, &opt);
+
+        action = NUL;
+        if (*w == '+' || *w == '-')
+            action = *(w++);
+
+        if (strcEQ(w, "SSLv2"))
+            thisopt = SSL_PROTOCOL_SSLV2;
+        else if (strcEQ(w, "SSLv3"))
+            thisopt = SSL_PROTOCOL_SSLV3;
+        else if (strcEQ(w, "TLSv1"))
+            thisopt = SSL_PROTOCOL_TLSV1;
+        else if (strcEQ(w, "all"))
+            thisopt = SSL_PROTOCOL_ALL;
+        else
+            return ap_pstrcat(cmd->pool, "SSLProxyProtocol: "
+                              "Illegal protocol '", w, "'", NULL);
+        if (action == '-')
+            options &= ~thisopt;
+        else if (action == '+')
+            options |= thisopt;
+        else
+            options = thisopt;
+    }
+    sc->nProxyProtocol = options;
+    return NULL;
+}
+
+const char *ssl_cmd_SSLProxyCipherSuite(
+    cmd_parms *cmd, char *struct_ptr, char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+
+    sc->szProxyCipherSuite = arg;
+    return NULL;
+}
+
+const char *ssl_cmd_SSLProxyVerify(
+    cmd_parms *cmd, char *struct_ptr, int flag)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+
+    sc->bProxyVerify = (flag ? TRUE : FALSE);
+    return NULL;
+}
+
+const char *ssl_cmd_SSLProxyVerifyDepth(
+    cmd_parms *cmd, char *struct_ptr, char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    int d;
+
+    d = atoi(arg);
+    if (d < 0)
+        return "SSLProxyVerifyDepth: Invalid argument";
+    sc->nProxyVerifyDepth = d;
+    return NULL;
+}
+
+const char *ssl_cmd_SSLProxyCACertificateFile(
+    cmd_parms *cmd, char *struct_ptr, char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    char *cpPath;
+
+    cpPath = ssl_util_server_root_relative(cmd->pool, "certkey", arg);
+    if (!ssl_util_path_check(SSL_PCM_EXISTS|SSL_PCM_ISREG|SSL_PCM_ISNONZERO, cpPath))
+        return ap_pstrcat(cmd->pool, "SSLProxyCACertificateFile: file '",
+                          cpPath, "' not exists or empty", NULL);
+    sc->szProxyCACertificateFile = cpPath;
+    return NULL;
+}
+
+const char *ssl_cmd_SSLProxyCACertificatePath(
+    cmd_parms *cmd, char *struct_ptr, char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    char *cpPath;
+
+    cpPath = ssl_util_server_root_relative(cmd->pool, "certkey", arg);
+    if (!ssl_util_path_check(SSL_PCM_EXISTS|SSL_PCM_ISDIR, cpPath))
+        return ap_pstrcat(cmd->pool, "SSLProxyCACertificatePath: directory '",
+                          cpPath, "' does not exists", NULL);
+    sc->szProxyCACertificatePath = cpPath;
+    return NULL;
+}
+
+const char *ssl_cmd_SSLProxyMachineCertificateFile(
+    cmd_parms *cmd, char *struct_ptr, char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    char *cpPath;
+
+    cpPath = ssl_util_server_root_relative(cmd->pool, "certkey", arg);
+    if (!ssl_util_path_check(SSL_PCM_EXISTS|SSL_PCM_ISREG|SSL_PCM_ISNONZERO, cpPath))
+        return ap_pstrcat(cmd->pool, "SSLProxyMachineCertFile: file '",
+                          cpPath, "' not exists or empty", NULL);
+    sc->szProxyClientCertificateFile = cpPath;
+    return NULL;
+}
+
+const char *ssl_cmd_SSLProxyMachineCertificatePath(
+    cmd_parms *cmd, char *struct_ptr, char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    char *cpPath;
+
+    cpPath = ssl_util_server_root_relative(cmd->pool, "certkey", arg);
+    if (!ssl_util_path_check(SSL_PCM_EXISTS|SSL_PCM_ISDIR, cpPath))
+        return ap_pstrcat(cmd->pool, "SSLProxyMachineCertPath: directory '",
+                          cpPath, "' does not exists", NULL);
+    sc->szProxyClientCertificatePath = cpPath;
+    return NULL;
+}
+
+#endif /* SSL_EXPERIMENTAL_PROXY */
+
diff --git a/modules/ssl/ssl_engine_dh.c b/modules/ssl/ssl_engine_dh.c
new file mode 100644 (file)
index 0000000..84f49e6
--- /dev/null
@@ -0,0 +1,255 @@
+#if 0
+=pod
+#endif
+/*                      _             _
+**  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
+** | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
+** | | | | | | (_) | (_| |   \__ \__ \ |  www.modssl.org
+** |_| |_| |_|\___/ \__,_|___|___/___/_|  ftp.modssl.org
+**                      |_____|
+** ssl_engine_dh.c 
+** Diffie-Hellman Built-in Temporary Parameters
+*/
+
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ *    nor may "mod_ssl" appear in their names without prior
+ *    written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include "mod_ssl.h"
+
+/* ----BEGIN GENERATED SECTION-------- */
+
+/*
+** Diffie-Hellman-Parameters: (512 bit)
+**     prime:
+**         00:d4:bc:d5:24:06:f6:9b:35:99:4b:88:de:5d:b8:
+**         96:82:c8:15:7f:62:d8:f3:36:33:ee:57:72:f1:1f:
+**         05:ab:22:d6:b5:14:5b:9f:24:1e:5a:cc:31:ff:09:
+**         0a:4b:c7:11:48:97:6f:76:79:50:94:e7:1e:79:03:
+**         52:9f:5a:82:4b
+**     generator: 2 (0x2)
+** Diffie-Hellman-Parameters: (1024 bit)
+**     prime:
+**         00:e6:96:9d:3d:49:5b:e3:2c:7c:f1:80:c3:bd:d4:
+**         79:8e:91:b7:81:82:51:bb:05:5e:2a:20:64:90:4a:
+**         79:a7:70:fa:15:a2:59:cb:d5:23:a6:a6:ef:09:c4:
+**         30:48:d5:a2:2f:97:1f:3c:20:12:9b:48:00:0e:6e:
+**         dd:06:1c:bc:05:3e:37:1d:79:4e:53:27:df:61:1e:
+**         bb:be:1b:ac:9b:5c:60:44:cf:02:3d:76:e0:5e:ea:
+**         9b:ad:99:1b:13:a6:3c:97:4e:9e:f1:83:9e:b5:db:
+**         12:51:36:f7:26:2e:56:a8:87:15:38:df:d8:23:c6:
+**         50:50:85:e2:1f:0d:d5:c8:6b
+**     generator: 2 (0x2)
+*/
+
+static unsigned char dh512_p[] =
+{
+    0xD4, 0xBC, 0xD5, 0x24, 0x06, 0xF6, 0x9B, 0x35, 0x99, 0x4B, 0x88, 0xDE,
+    0x5D, 0xB8, 0x96, 0x82, 0xC8, 0x15, 0x7F, 0x62, 0xD8, 0xF3, 0x36, 0x33,
+    0xEE, 0x57, 0x72, 0xF1, 0x1F, 0x05, 0xAB, 0x22, 0xD6, 0xB5, 0x14, 0x5B,
+    0x9F, 0x24, 0x1E, 0x5A, 0xCC, 0x31, 0xFF, 0x09, 0x0A, 0x4B, 0xC7, 0x11,
+    0x48, 0x97, 0x6F, 0x76, 0x79, 0x50, 0x94, 0xE7, 0x1E, 0x79, 0x03, 0x52,
+    0x9F, 0x5A, 0x82, 0x4B,
+};
+static unsigned char dh512_g[] =
+{
+    0x02,
+};
+
+static DH *get_dh512()
+{
+    DH *dh;
+
+    if ((dh = DH_new()) == NULL)
+        return (NULL);
+    dh->p = BN_bin2bn(dh512_p, sizeof(dh512_p), NULL);
+    dh->g = BN_bin2bn(dh512_g, sizeof(dh512_g), NULL);
+    if ((dh->p == NULL) || (dh->g == NULL))
+        return (NULL);
+    return (dh);
+}
+static unsigned char dh1024_p[] =
+{
+    0xE6, 0x96, 0x9D, 0x3D, 0x49, 0x5B, 0xE3, 0x2C, 0x7C, 0xF1, 0x80, 0xC3,
+    0xBD, 0xD4, 0x79, 0x8E, 0x91, 0xB7, 0x81, 0x82, 0x51, 0xBB, 0x05, 0x5E,
+    0x2A, 0x20, 0x64, 0x90, 0x4A, 0x79, 0xA7, 0x70, 0xFA, 0x15, 0xA2, 0x59,
+    0xCB, 0xD5, 0x23, 0xA6, 0xA6, 0xEF, 0x09, 0xC4, 0x30, 0x48, 0xD5, 0xA2,
+    0x2F, 0x97, 0x1F, 0x3C, 0x20, 0x12, 0x9B, 0x48, 0x00, 0x0E, 0x6E, 0xDD,
+    0x06, 0x1C, 0xBC, 0x05, 0x3E, 0x37, 0x1D, 0x79, 0x4E, 0x53, 0x27, 0xDF,
+    0x61, 0x1E, 0xBB, 0xBE, 0x1B, 0xAC, 0x9B, 0x5C, 0x60, 0x44, 0xCF, 0x02,
+    0x3D, 0x76, 0xE0, 0x5E, 0xEA, 0x9B, 0xAD, 0x99, 0x1B, 0x13, 0xA6, 0x3C,
+    0x97, 0x4E, 0x9E, 0xF1, 0x83, 0x9E, 0xB5, 0xDB, 0x12, 0x51, 0x36, 0xF7,
+    0x26, 0x2E, 0x56, 0xA8, 0x87, 0x15, 0x38, 0xDF, 0xD8, 0x23, 0xC6, 0x50,
+    0x50, 0x85, 0xE2, 0x1F, 0x0D, 0xD5, 0xC8, 0x6B,
+};
+static unsigned char dh1024_g[] =
+{
+    0x02,
+};
+
+static DH *get_dh1024()
+{
+    DH *dh;
+
+    if ((dh = DH_new()) == NULL)
+        return (NULL);
+    dh->p = BN_bin2bn(dh1024_p, sizeof(dh1024_p), NULL);
+    dh->g = BN_bin2bn(dh1024_g, sizeof(dh1024_g), NULL);
+    if ((dh->p == NULL) || (dh->g == NULL))
+        return (NULL);
+    return (dh);
+}
+/* ----END GENERATED SECTION---------- */
+
+DH *ssl_dh_GetTmpParam(int nKeyLen)
+{
+    DH *dh;
+
+    if (nKeyLen == 512)
+        dh = get_dh512();
+    else if (nKeyLen == 1024)
+        dh = get_dh1024();
+    else
+        dh = get_dh1024();
+    return dh;
+}
+
+DH *ssl_dh_GetParamFromFile(char *file)
+{
+    DH *dh = NULL;
+    BIO *bio;
+
+    if ((bio = BIO_new_file(file, "r")) == NULL)
+        return NULL;
+#if SSL_LIBRARY_VERSION < 0x00904000
+    dh = PEM_read_bio_DHparams(bio, NULL, NULL);
+#else
+    dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
+#endif
+    BIO_free(bio);
+    return (dh);
+}
+
+/*
+=cut
+##
+##  Embedded Perl script for generating the temporary DH parameters
+##
+
+require 5.003;
+use strict;
+
+#   configuration
+my $file  = $0;
+my $begin = '----BEGIN GENERATED SECTION--------';
+my $end   = '----END GENERATED SECTION----------';
+
+#   read ourself and keep a backup
+open(FP, "<$file") || die;
+my $source = '';
+$source .= $_ while (<FP>);
+close(FP);
+open(FP, ">$file.bak") || die;
+print FP $source;
+close(FP);
+
+#   generate the DH parameters
+print "1. Generate 512 and 1024 bit Diffie-Hellman parameters (p, g)\n";
+my $rand = '';
+foreach $file (qw(/var/log/messages /var/adm/messages 
+                  /kernel /vmunix /vmlinuz /etc/hosts /etc/resolv.conf)) {
+    if (-f $file) {
+        $rand = $file     if ($rand eq '');
+        $rand .= ":$file" if ($rand ne '');
+    }
+}
+$rand = "-rand $rand" if ($rand ne '');
+system("openssl gendh $rand -out dh512.pem 512");
+system("openssl gendh $rand -out dh1024.pem 1024");
+
+#   generate DH param info 
+my $dhinfo = '';
+open(FP, "openssl dh -noout -text -in dh512.pem |") || die;
+$dhinfo .= $_ while (<FP>);
+close(FP);
+open(FP, "openssl dh -noout -text -in dh1024.pem |") || die;
+$dhinfo .= $_ while (<FP>);
+close(FP);
+$dhinfo =~ s|^|** |mg;
+$dhinfo = "\n\/\*\n$dhinfo\*\/\n\n";
+
+#   generate C source from DH params
+my $dhsource = '';
+open(FP, "openssl dh -noout -C -in dh512.pem | indent | expand -8 |") || die;
+$dhsource .= $_ while (<FP>);
+close(FP);
+open(FP, "openssl dh -noout -C -in dh1024.pem | indent | expand -8 |") || die;
+$dhsource .= $_ while (<FP>);
+close(FP);
+$dhsource =~ s|(DH\s+\*get_dh)|static $1|sg;
+
+#   generate output
+my $o = $dhinfo . $dhsource;
+
+#   insert the generated code at the target location
+$source =~ s|(\/\* $begin.+?\n).*\n(.*?\/\* $end)|$1$o$2|s;
+
+#   and update the source on disk
+print "Updating file `$file'\n";
+open(FP, ">$file") || die;
+print FP $source;
+close(FP);
+
+#   cleanup
+unlink("dh512.pem");
+unlink("dh1024.pem");
+
+=pod
+*/
diff --git a/modules/ssl/ssl_engine_ds.c b/modules/ssl/ssl_engine_ds.c
new file mode 100644 (file)
index 0000000..f0f9e00
--- /dev/null
@@ -0,0 +1,195 @@
+/*                      _             _
+**  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
+** | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
+** | | | | | | (_) | (_| |   \__ \__ \ |  www.modssl.org
+** |_| |_| |_|\___/ \__,_|___|___/___/_|  ftp.modssl.org
+**                      |_____|
+**  ssl_engine_ds.c
+**  Additional Data Structures
+*/
+
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ *    nor may "mod_ssl" appear in their names without prior
+ *    written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+                             /* ``If you can't do it in
+                                  C or assembly language,
+                                  it isn't worth doing.''
+                                         -- Unknown         */
+#include "mod_ssl.h"
+
+
+/*  _________________________________________________________________
+**
+**  Data Structures which store _arbitrary_ data
+**  _________________________________________________________________
+*/
+
+ssl_ds_array *ssl_ds_array_make(pool *p, int size)
+{
+    ssl_ds_array *a;
+
+    if ((a = (ssl_ds_array *)ap_palloc(p, sizeof(ssl_ds_array))) == NULL)
+        return NULL;
+    a->pPool = p;
+    if ((a->pSubPool = ap_make_sub_pool(p)) == NULL)
+        return NULL;
+    a->aData   = ap_make_array(a->pSubPool, 2, size);
+    return a;
+}
+
+BOOL ssl_ds_array_isempty(ssl_ds_array *a)
+{
+    if (a == NULL || a->aData == NULL || a->aData->nelts == 0)
+        return TRUE;
+    else
+        return FALSE;
+}
+
+void *ssl_ds_array_push(ssl_ds_array *a)
+{
+    void *d;
+
+    d = (void *)ap_push_array(a->aData);
+    return d;
+}
+
+void *ssl_ds_array_get(ssl_ds_array *a, int n)
+{
+    void *d;
+
+    if (n < 0 || n >= a->aData->nelts)
+        return NULL;
+    d = (void *)(a->aData->elts+(a->aData->elt_size*n));
+    return d;
+}
+
+void ssl_ds_array_wipeout(ssl_ds_array *a)
+{
+    if (a->aData->nelts > 0)
+        memset(a->aData->elts, 0, a->aData->elt_size*a->aData->nelts);
+    return;
+}
+
+void ssl_ds_array_kill(ssl_ds_array *a)
+{
+    ap_destroy_pool(a->pSubPool);
+    a->pSubPool = NULL;
+    a->aData    = NULL;
+    return;
+}
+
+ssl_ds_table *ssl_ds_table_make(pool *p, int size)
+{
+    ssl_ds_table *t;
+
+    if ((t = (ssl_ds_table *)ap_palloc(p, sizeof(ssl_ds_table))) == NULL)
+        return NULL;
+    t->pPool = p;
+    if ((t->pSubPool = ap_make_sub_pool(p)) == NULL)
+        return NULL;
+    t->aKey  = ap_make_array(t->pSubPool, 2, MAX_STRING_LEN);
+    t->aData = ap_make_array(t->pSubPool, 2, size);
+    return t;
+}
+
+BOOL ssl_ds_table_isempty(ssl_ds_table *t)
+{
+    if (t == NULL || t->aKey == NULL || t->aKey->nelts == 0)
+        return TRUE;
+    else
+        return FALSE;
+}
+
+void *ssl_ds_table_push(ssl_ds_table *t, char *key)
+{
+    char *k;
+    void *d;
+
+    k = (char *)ap_push_array(t->aKey);
+    d = (void *)ap_push_array(t->aData);
+    ap_cpystrn(k, key, t->aKey->elt_size);
+    return d;
+}
+
+void *ssl_ds_table_get(ssl_ds_table *t, char *key)
+{
+    char *k;
+    void *d;
+    int i;
+
+    d = NULL;
+    for (i = 0; i < t->aKey->nelts; i++) {
+        k = (t->aKey->elts+(t->aKey->elt_size*i));
+        if (strEQ(k, key)) {
+            d = (void *)(t->aData->elts+(t->aData->elt_size*i));
+            break;
+        }
+    }
+    return d;
+}
+
+void ssl_ds_table_wipeout(ssl_ds_table *t)
+{
+    if (t->aKey->nelts > 0) {
+        memset(t->aKey->elts, 0, t->aKey->elt_size*t->aKey->nelts);
+        memset(t->aData->elts, 0, t->aData->elt_size*t->aData->nelts);
+    }
+    return;
+}
+
+void ssl_ds_table_kill(ssl_ds_table *t)
+{
+    ap_destroy_pool(t->pSubPool);
+    t->pSubPool = NULL;
+    t->aKey     = NULL;
+    t->aData    = NULL;
+    return;
+}
+
diff --git a/modules/ssl/ssl_engine_ext.c b/modules/ssl/ssl_engine_ext.c
new file mode 100644 (file)
index 0000000..81b283c
--- /dev/null
@@ -0,0 +1,808 @@
+/*                      _             _
+**  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
+** | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
+** | | | | | | (_) | (_| |   \__ \__ \ |  www.modssl.org
+** |_| |_| |_|\___/ \__,_|___|___/___/_|  ftp.modssl.org
+**                      |_____|
+**  ssl_engine_ext.c
+**  Extensions to other Apache parts
+*/
+
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ *    nor may "mod_ssl" appear in their names without prior
+ *    written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+                             /* ``Only those who attempt the absurd
+                                  can achieve the impossible.''
+                                           -- Unknown             */
+#include "mod_ssl.h"
+
+
+/*  _________________________________________________________________
+**
+**  SSL Extensions
+**  _________________________________________________________________
+*/
+
+static void  ssl_ext_mlc_register(void);
+static void  ssl_ext_mlc_unregister(void);
+static void  ssl_ext_mr_register(void);
+static void  ssl_ext_mr_unregister(void);
+static void  ssl_ext_mp_register(void);
+static void  ssl_ext_mp_unregister(void);
+static void  ssl_ext_ms_register(void);
+static void  ssl_ext_ms_unregister(void);
+
+void ssl_ext_register(void)
+{
+    ssl_ext_mlc_register();
+    ssl_ext_mr_register();
+    ssl_ext_mp_register();
+    ssl_ext_ms_register();
+    return;
+}
+
+void ssl_ext_unregister(void)
+{
+    ssl_ext_mlc_unregister();
+    ssl_ext_mr_unregister();
+    ssl_ext_mp_unregister();
+    ssl_ext_ms_unregister();
+    return;
+}
+
+/*  _________________________________________________________________
+**
+**  SSL Extension to mod_log_config
+**  _________________________________________________________________
+*/
+
+static char *ssl_ext_mlc_log_c(request_rec *r, char *a);
+static char *ssl_ext_mlc_log_x(request_rec *r, char *a);
+
+/*
+ * register us for the mod_log_config function registering phase
+ * to establish %{...}c and to be able to expand %{...}x variables.
+ */
+static void ssl_ext_mlc_register(void)
+{
+    ap_hook_register("ap::mod_log_config::log_c",
+                     ssl_ext_mlc_log_c, AP_HOOK_NOCTX);
+    ap_hook_register("ap::mod_log_config::log_x",
+                     ssl_ext_mlc_log_x, AP_HOOK_NOCTX);
+    return;
+}
+
+static void ssl_ext_mlc_unregister(void)
+{
+    ap_hook_unregister("ap::mod_log_config::log_c",
+                       ssl_ext_mlc_log_c);
+    ap_hook_unregister("ap::mod_log_config::log_x",
+                       ssl_ext_mlc_log_x);
+    return;
+}
+
+/*
+ * implement the %{..}c log function
+ * (we are the only function)
+ */
+static char *ssl_ext_mlc_log_c(request_rec *r, char *a)
+{
+    char *result;
+
+    if (ap_ctx_get(r->connection->client->ctx, "ssl") == NULL)
+        return NULL;
+    result = NULL;
+    if (strEQ(a, "version"))
+        result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_PROTOCOL");
+    else if (strEQ(a, "cipher"))
+        result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CIPHER");
+    else if (strEQ(a, "subjectdn") || strEQ(a, "clientcert"))
+        result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CLIENT_S_DN");
+    else if (strEQ(a, "issuerdn") || strEQ(a, "cacert"))
+        result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CLIENT_I_DN");
+    else if (strEQ(a, "errcode"))
+        result = "-";
+    else if (strEQ(a, "errstr"))
+        result = ap_ctx_get(r->connection->client->ctx, "ssl::verify::error");
+    if (result != NULL && result[0] == NUL)
+        result = NULL;
+    return result;
+}
+
+/*
+ * extend the implementation of the %{..}x log function
+ * (there can be more functions)
+ */
+static char *ssl_ext_mlc_log_x(request_rec *r, char *a)
+{
+    char *result;
+
+    result = NULL;
+    if (ap_ctx_get(r->connection->client->ctx, "ssl") != NULL)
+        result = ssl_var_lookup(r->pool, r->server, r->connection, r, a);
+    if (result != NULL && result[0] == NUL)
+        result = NULL;
+    return result;
+}
+
+/*  _________________________________________________________________
+**
+**  SSL Extension to mod_rewrite
+**  _________________________________________________________________
+*/
+
+static char *ssl_ext_mr_lookup_variable(request_rec *r, char *var);
+
+/*
+ * register us for the mod_rewrite lookup_variable() function
+ */
+static void ssl_ext_mr_register(void)
+{
+    ap_hook_register("ap::mod_rewrite::lookup_variable",
+                     ssl_ext_mr_lookup_variable, AP_HOOK_NOCTX);
+    return;
+}
+
+static void ssl_ext_mr_unregister(void)
+{
+    ap_hook_unregister("ap::mod_rewrite::lookup_variable",
+                       ssl_ext_mr_lookup_variable);
+    return;
+}
+
+static char *ssl_ext_mr_lookup_variable(request_rec *r, char *var)
+{
+    char *val;
+
+    val = ssl_var_lookup(r->pool, r->server, r->connection, r, var);
+    if (val[0] == NUL)
+        val = NULL;
+    return val;
+}
+
+/*  _________________________________________________________________
+**
+**  SSL Extension to mod_proxy
+**  _________________________________________________________________
+*/
+
+static int   ssl_ext_mp_canon(request_rec *, char *);
+static int   ssl_ext_mp_handler(request_rec *, void *, char *, char *, int, char *);
+static int   ssl_ext_mp_set_destport(request_rec *);
+static char *ssl_ext_mp_new_connection(request_rec *, BUFF *, char *);
+static void  ssl_ext_mp_close_connection(void *);
+static int   ssl_ext_mp_write_host_header(request_rec *, BUFF *, char *, int, char *);
+#ifdef SSL_EXPERIMENTAL_PROXY
+static void  ssl_ext_mp_init(server_rec *, pool *);
+static int   ssl_ext_mp_verify_cb(int, X509_STORE_CTX *);
+static int   ssl_ext_mp_clientcert_cb(SSL *, X509 **, EVP_PKEY **);
+#endif
+
+/*
+ * register us ...
+ */
+static void ssl_ext_mp_register(void)
+{
+#ifdef SSL_EXPERIMENTAL_PROXY
+    ap_hook_register("ap::mod_proxy::init",
+                     ssl_ext_mp_init, AP_HOOK_NOCTX);
+#endif
+    ap_hook_register("ap::mod_proxy::canon",
+                     ssl_ext_mp_canon, AP_HOOK_NOCTX);
+    ap_hook_register("ap::mod_proxy::handler",
+                     ssl_ext_mp_handler, AP_HOOK_NOCTX);
+    ap_hook_register("ap::mod_proxy::http::handler::set_destport",
+                     ssl_ext_mp_set_destport, AP_HOOK_NOCTX);
+    ap_hook_register("ap::mod_proxy::http::handler::new_connection",
+                     ssl_ext_mp_new_connection, AP_HOOK_NOCTX);
+    ap_hook_register("ap::mod_proxy::http::handler::write_host_header",
+                     ssl_ext_mp_write_host_header, AP_HOOK_NOCTX);
+    return;
+}
+
+static void ssl_ext_mp_unregister(void)
+{
+#ifdef SSL_EXPERIMENTAL_PROXY
+    ap_hook_unregister("ap::mod_proxy::init", ssl_ext_mp_init);
+#endif
+    ap_hook_unregister("ap::mod_proxy::canon", ssl_ext_mp_canon);
+    ap_hook_unregister("ap::mod_proxy::handler", ssl_ext_mp_handler);
+    ap_hook_unregister("ap::mod_proxy::http::handler::set_destport",
+                       ssl_ext_mp_set_destport);
+    ap_hook_unregister("ap::mod_proxy::http::handler::new_connection",
+                       ssl_ext_mp_new_connection);
+    ap_hook_unregister("ap::mod_proxy::http::handler::write_host_header",
+                       ssl_ext_mp_write_host_header);
+    return;
+}
+
+/*
+ * SSL proxy initialization
+ */
+#ifdef SSL_EXPERIMENTAL_PROXY
+static void ssl_ext_mp_init(server_rec *s, pool *p)
+{
+    SSLSrvConfigRec *sc;
+    char *cpVHostID;
+    int nVerify;
+    SSL_CTX *ctx;
+    char *cp;
+    STACK_OF(X509_INFO) *sk;
+
+    /*
+     * Initialize each virtual server 
+     */
+    ERR_clear_error();
+    for (; s != NULL; s = s->next) {
+        sc = mySrvConfig(s);
+        cpVHostID = ssl_util_vhostid(p, s);
+        
+        if (sc->bProxyVerify == UNSET)
+            sc->bProxyVerify = FALSE;
+
+        /*
+         *  Create new SSL context and configure callbacks
+         */
+        if (sc->nProxyProtocol == SSL_PROTOCOL_NONE) {
+            ssl_log(s, SSL_LOG_ERROR,
+                    "Init: (%s) No Proxy SSL protocols available [hint: SSLProxyProtocol]",
+                    cpVHostID);
+            ssl_die();
+        }
+        cp = ap_pstrcat(p, (sc->nProxyProtocol & SSL_PROTOCOL_SSLV2 ? "SSLv2, " : ""), 
+                           (sc->nProxyProtocol & SSL_PROTOCOL_SSLV3 ? "SSLv3, " : ""), 
+                           (sc->nProxyProtocol & SSL_PROTOCOL_TLSV1 ? "TLSv1, " : ""), NULL);
+        cp[strlen(cp)-2] = NUL;
+        ssl_log(s, SSL_LOG_TRACE, 
+                "Init: (%s) Creating new proxy SSL context (protocols: %s)", 
+                cpVHostID, cp);
+        if (sc->nProxyProtocol == SSL_PROTOCOL_SSLV2)
+            ctx = SSL_CTX_new(SSLv2_client_method());  /* only SSLv2 is left */ 
+        else
+            ctx = SSL_CTX_new(SSLv23_client_method()); /* be more flexible */
+        if (ctx == NULL) {
+            ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+                    "Init: (%s) Unable to create SSL Proxy context", cpVHostID);
+            ssl_die();
+        }
+        sc->pSSLProxyCtx = ctx;
+        SSL_CTX_set_options(ctx, SSL_OP_ALL);
+        if (!(sc->nProxyProtocol & SSL_PROTOCOL_SSLV2))
+            SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2);
+        if (!(sc->nProxyProtocol & SSL_PROTOCOL_SSLV3))
+            SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3);
+        if (!(sc->nProxyProtocol & SSL_PROTOCOL_TLSV1)) 
+            SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1);
+
+        if (sc->szProxyClientCertificateFile || sc->szProxyClientCertificatePath) {
+            sk = sk_X509_INFO_new_null();
+            if (sc->szProxyClientCertificateFile) 
+                SSL_load_CrtAndKeyInfo_file(p, sk, sc->szProxyClientCertificateFile);
+            if (sc->szProxyClientCertificatePath)
+                SSL_load_CrtAndKeyInfo_path(p, sk, sc->szProxyClientCertificatePath);
+            ssl_log(s, SSL_LOG_TRACE, "Init: (%s) loaded %d client certs for SSL proxy",
+                    cpVHostID, sk_X509_INFO_num(sk));
+            if (sk_X509_INFO_num(sk) > 0) {
+                SSL_CTX_set_client_cert_cb(ctx, ssl_ext_mp_clientcert_cb);
+                sc->skProxyClientCerts = sk;
+            }
+        }
+
+        /*
+         * Calculate OpenSSL verify type for verifying the remote server
+         * certificate. We either verify it against our list of CA's, or don't
+         * bother at all.
+         */
+        nVerify = SSL_VERIFY_NONE;
+        if (sc->bProxyVerify)
+            nVerify |= SSL_VERIFY_PEER;
+        if (   nVerify & SSL_VERIFY_PEER 
+            && sc->szProxyCACertificateFile == NULL 
+            && sc->szProxyCACertificatePath == NULL) {
+            ssl_log(s, SSL_LOG_ERROR,
+                    "Init: (%s) SSLProxyVerify set to On but no CA certificates configured",
+                    cpVHostID);
+            ssl_die();
+        }
+        if (   nVerify & SSL_VERIFY_NONE
+            && (   sc->szProxyCACertificateFile != NULL
+                || sc->szProxyCACertificatePath != NULL)) {
+            ssl_log(s, SSL_LOG_WARN, 
+                    "init: (%s) CA certificates configured but ignored because SSLProxyVerify is Off",
+                    cpVHostID);
+        }
+        SSL_CTX_set_verify(ctx, nVerify, ssl_ext_mp_verify_cb);
+
+        /*
+         * Enable session caching. We can safely use the same cache
+         * as used for communicating with the other clients.
+         */
+        SSL_CTX_sess_set_new_cb(sc->pSSLProxyCtx,    ssl_callback_NewSessionCacheEntry);
+        SSL_CTX_sess_set_get_cb(sc->pSSLProxyCtx,    ssl_callback_GetSessionCacheEntry);
+        SSL_CTX_sess_set_remove_cb(sc->pSSLProxyCtx, ssl_callback_DelSessionCacheEntry);
+
+        /*
+         *  Configure SSL Cipher Suite
+         */
+        ssl_log(s, SSL_LOG_TRACE,
+                "Init: (%s) Configuring permitted SSL ciphers for SSL proxy", cpVHostID);
+        if (sc->szProxyCipherSuite != NULL) {
+            if (!SSL_CTX_set_cipher_list(sc->pSSLProxyCtx, sc->szProxyCipherSuite)) {
+                ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+                        "Init: (%s) Unable to configure permitted SSL ciphers for SSL Proxy",
+                        cpVHostID);
+                ssl_die();
+            }
+        }
+
+        /*
+         * Configure Client Authentication details
+         */
+        if (sc->szProxyCACertificateFile != NULL || sc->szProxyCACertificatePath != NULL) {
+             ssl_log(s, SSL_LOG_DEBUG, 
+                     "Init: (%s) Configuring client verification locations for SSL Proxy", 
+                     cpVHostID);
+             if (!SSL_CTX_load_verify_locations(sc->pSSLProxyCtx,
+                                                sc->szProxyCACertificateFile,
+                                                sc->szProxyCACertificatePath)) {
+                 ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR, 
+                         "Init: (%s) Unable to configure SSL verify locations for SSL proxy",
+                         cpVHostID);
+                 ssl_die();
+             }
+        }
+    }
+    return;
+}
+#endif /* SSL_EXPERIMENTAL_PROXY */
+
+static int ssl_ext_mp_canon(request_rec *r, char *url)
+{
+    int rc;
+
+    if (strcEQn(url, "https:", 6)) {
+        rc = OK;
+        ap_hook_call("ap::mod_proxy::http::canon",
+                     &rc, r, url+6, "https", DEFAULT_HTTPS_PORT);
+        return rc;
+    }
+    return DECLINED;
+}
+
+static int ssl_ext_mp_handler(
+    request_rec *r, void *cr, char *url, char *proxyhost, int proxyport, char *protocol)
+{
+    int rc;
+
+    if (strcEQ(protocol, "https")) {
+        ap_ctx_set(r->ctx, "ssl::proxy::enabled", PTRUE);
+        ap_hook_call("ap::mod_proxy::http::handler",
+                     &rc, r, cr, url, proxyhost, proxyport);
+        return rc;
+    }
+    else {
+        ap_ctx_set(r->ctx, "ssl::proxy::enabled", PFALSE);
+    }
+    return DECLINED;
+}
+
+static int ssl_ext_mp_set_destport(request_rec *r)
+{
+    if (ap_ctx_get(r->ctx, "ssl::proxy::enabled") == PTRUE)
+        return DEFAULT_HTTPS_PORT;
+    else
+        return DEFAULT_HTTP_PORT;
+}
+
+static char *ssl_ext_mp_new_connection(request_rec *r, BUFF *fb, char *peer)
+{
+#ifndef SSL_EXPERIMENTAL_PROXY
+    SSL_CTX *ssl_ctx;
+#endif
+    SSL *ssl;
+    char *errmsg;
+    int rc;
+    char *cpVHostID;
+    char *cpVHostMD5;
+#ifdef SSL_EXPERIMENTAL_PROXY
+    SSLSrvConfigRec *sc;
+    char *cp;
+#endif
+
+    if (ap_ctx_get(r->ctx, "ssl::proxy::enabled") == PFALSE)
+        return NULL;
+
+    /*
+     * Find context
+     */
+#ifdef SSL_EXPERIMENTAL_PROXY
+    sc = mySrvConfig(r->server);
+#endif
+    cpVHostID = ssl_util_vhostid(r->pool, r->server);
+
+    /*
+     * Create a SSL context and handle
+     */
+#ifdef SSL_EXPERIMENTAL_PROXY
+    ssl = SSL_new(sc->pSSLProxyCtx);
+#else
+    ssl_ctx = SSL_CTX_new(SSLv23_client_method());
+    ssl = SSL_new(ssl_ctx);
+#endif
+    if (ssl == NULL) {
+        errmsg = ap_psprintf(r->pool, "SSL proxy new failed (%s): peer %s: %s",
+                             cpVHostID, peer, ERR_reason_error_string(ERR_get_error()));
+        ap_ctx_set(fb->ctx, "ssl", NULL);
+        return errmsg;
+    }
+    SSL_clear(ssl);
+    cpVHostMD5 = ap_md5(r->pool, (unsigned char *)cpVHostID);
+    if (!SSL_set_session_id_context(ssl, (unsigned char *)cpVHostMD5, strlen(cpVHostMD5))) {
+        errmsg = ap_psprintf(r->pool, "Unable to set session id context to `%s': peer %s: %s",
+                             cpVHostMD5, peer, ERR_reason_error_string(ERR_get_error()));
+        ap_ctx_set(fb->ctx, "ssl", NULL);
+        return errmsg;
+    }
+    SSL_set_fd(ssl, fb->fd);
+#ifdef SSL_EXPERIMENTAL_PROXY
+    SSL_set_app_data(ssl, fb->ctx);
+#endif
+    ap_ctx_set(fb->ctx, "ssl", ssl);
+#ifdef SSL_EXPERIMENTAL_PROXY
+    ap_ctx_set(fb->ctx, "ssl::proxy::server_rec", r->server);
+    ap_ctx_set(fb->ctx, "ssl::proxy::peer", peer);
+    ap_ctx_set(fb->ctx, "ssl::proxy::servername", cpVHostID);
+    ap_ctx_set(fb->ctx, "ssl::proxy::verifyerror", NULL);
+#endif
+
+    /*
+     * Give us a chance to gracefully close the connection
+     */
+    ap_register_cleanup(r->pool, (void *)fb,
+                        ssl_ext_mp_close_connection, ssl_ext_mp_close_connection);
+
+    /*
+     * Establish the SSL connection
+     */
+    if ((rc = SSL_connect(ssl)) <= 0) {
+#ifdef SSL_EXPERIMENTAL_PROXY
+        if ((cp = (char *)ap_ctx_get(fb->ctx, "ssl::proxy::verifyerror")) != NULL) {
+            SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN); 
+            SSL_smart_shutdown(ssl);
+            SSL_free(ssl);
+            ap_ctx_set(fb->ctx, "ssl", NULL);
+            ap_bsetflag(fb, B_EOF|B_EOUT, 1);
+            return NULL;
+        }
+#endif
+        errmsg = ap_psprintf(r->pool, "SSL proxy connect failed (%s): peer %s: %s",
+                             cpVHostID, peer, ERR_reason_error_string(ERR_get_error()));
+        ssl_log(r->server, SSL_LOG_ERROR, errmsg);
+        SSL_free(ssl);
+        ap_ctx_set(fb->ctx, "ssl", NULL);
+        return errmsg;
+    }
+
+    return NULL;
+}
+
+static void ssl_ext_mp_close_connection(void *_fb)
+{
+    BUFF *fb = _fb;
+    SSL *ssl;
+#ifndef SSL_EXPERIMENTAL_PROXY
+    SSL_CTX *ctx;
+#endif
+
+    ssl = ap_ctx_get(fb->ctx, "ssl");
+    if (ssl != NULL) {
+#ifndef SSL_EXPERIMENTAL_PROXY
+        ctx = SSL_get_SSL_CTX(ssl);
+#endif
+        SSL_set_shutdown(ssl, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN);
+        SSL_smart_shutdown(ssl);
+        SSL_free(ssl);
+        ap_ctx_set(fb->ctx, "ssl", NULL);
+#ifndef SSL_EXPERIMENTAL_PROXY
+        if (ctx != NULL)
+            SSL_CTX_free(ctx);
+#endif
+    }
+    return;
+}
+
+static int ssl_ext_mp_write_host_header(
+    request_rec *r, BUFF *fb, char *host, int port, char *portstr)
+{
+    if (ap_ctx_get(r->ctx, "ssl::proxy::enabled") == PFALSE)
+        return DECLINED;
+
+    if (portstr != NULL && port != DEFAULT_HTTPS_PORT) {
+        ap_bvputs(fb, "Host: ", host, ":", portstr, "\r\n", NULL);
+        return OK;
+    }
+    return DECLINED;
+}
+
+#ifdef SSL_EXPERIMENTAL_PROXY
+
+/* 
+ * Callback for client certificate stuff.
+ * If the remote site sent us a SSLv3 list of acceptable CA's then trawl the
+ * table of client certs and send the first one that matches.
+ */
+static int ssl_ext_mp_clientcert_cb(SSL *ssl, X509 **x509, EVP_PKEY **pkey) 
+{
+    SSLSrvConfigRec *sc;
+    X509_NAME *xnx;
+    X509_NAME *issuer;
+    X509_INFO *xi;
+    char *peer;
+    char *servername;
+    server_rec *s;
+    ap_ctx *pCtx;
+    STACK_OF(X509_NAME) *sk;
+    STACK_OF(X509_INFO) *pcerts;
+    char *cp;
+    int i, j;
+    
+    pCtx       = (ap_ctx *)SSL_get_app_data(ssl);
+    s          = ap_ctx_get(pCtx, "ssl::proxy::server_rec");
+    peer       = ap_ctx_get(pCtx, "ssl::proxy::peer");
+    servername = ap_ctx_get(pCtx, "ssl::proxy::servername");
+
+    sc         = mySrvConfig(s);
+    pcerts     = sc->skProxyClientCerts;
+
+    ssl_log(s, SSL_LOG_DEBUG, 
+            "Proxy client certificate callback: (%s) entered", servername);
+
+    if ((pcerts == NULL) || (sk_X509_INFO_num(pcerts) <= 0)) {
+        ssl_log(s, SSL_LOG_TRACE,
+                "Proxy client certificate callback: (%s) "
+                "site wanted client certificate but none available", 
+                servername);
+        return 0;
+    }                                                                     
+
+    sk = SSL_get_client_CA_list(ssl);
+
+    if ((sk == NULL) || (sk_X509_NAME_num(sk) <= 0)) {
+        /* 
+         * remote site didn't send us a list of acceptable CA certs, 
+         * so lets send the first one we came across 
+         */   
+        xi = sk_X509_INFO_value(pcerts, 0);
+        cp  = X509_NAME_oneline(X509_get_subject_name(xi->x509), NULL, 0);
+        ssl_log(s, SSL_LOG_DEBUG,
+                "SSL Proxy: (%s) no acceptable CA list, sending %s", 
+                servername, cp != NULL ? cp : "-unknown-");
+        free(cp);
+        /* export structures to the caller */
+        *x509 = xi->x509;
+        *pkey = xi->x_pkey->dec_pkey;
+        /* prevent OpenSSL freeing these structures */
+        CRYPTO_add(&((*x509)->references), +1, CRYPTO_LOCK_X509_PKEY);
+        CRYPTO_add(&((*pkey)->references), +1, CRYPTO_LOCK_X509_PKEY);
+        return 1;
+    }         
+
+    for (i = 0; i < sk_X509_NAME_num(sk); i++) {
+        xnx = sk_X509_NAME_value(sk, i);
+        for (j = 0; j < sk_X509_INFO_num(pcerts); j++) {
+            xi = sk_X509_INFO_value(pcerts,j);
+            issuer = X509_get_issuer_name(xi->x509);
+            if (X509_NAME_cmp(issuer, xnx) == 0) {
+                cp = X509_NAME_oneline(X509_get_subject_name(xi->x509), NULL, 0);
+                ssl_log(s, SSL_LOG_DEBUG, "SSL Proxy: (%s) sending %s", 
+                        servername, cp != NULL ? cp : "-unknown-");
+                free(cp);
+                /* export structures to the caller */
+                *x509 = xi->x509;
+                *pkey = xi->x_pkey->dec_pkey;
+                /* prevent OpenSSL freeing these structures */
+                CRYPTO_add(&((*x509)->references), +1, CRYPTO_LOCK_X509_PKEY);
+                CRYPTO_add(&((*pkey)->references), +1, CRYPTO_LOCK_X509_PKEY);
+                return 1;
+            }
+        }
+    }
+    ssl_log(s, SSL_LOG_TRACE,
+            "Proxy client certificate callback: (%s) "
+            "no client certificate found!?", servername);
+    return 0; 
+}
+
+/*
+ * This is the verify callback when we are connecting to a remote SSL server
+ * from the proxy. Information is passed in via the SSL "ctx" app_data
+ * mechanism. We pass in an Apache context in this field, which contains
+ * server_rec of the server making the proxy connection from the
+ * "ssl::proxy::server_rec" context.
+ *
+ * The result of the verification is passed back out to SSLERR via the return
+ * value. We also store the error message in the "proxyverifyfailed" context,
+ * so the caller of SSL_connect() can log a detailed error message.
+ */
+static int ssl_ext_mp_verify_cb(int ok, X509_STORE_CTX *ctx)
+{
+    SSLSrvConfigRec *sc;
+    X509 *xs;
+    int errnum;
+    int errdepth;
+    char *cp, *cp2;
+    ap_ctx *pCtx;
+    server_rec *s;
+    SSL *ssl;
+    char *peer;
+    char *servername;
+
+    ssl        = (SSL *)X509_STORE_CTX_get_app_data(ctx);
+    pCtx       = (ap_ctx *)SSL_get_app_data(ssl);
+    s          = ap_ctx_get(pCtx, "ssl::proxy::server_rec");
+    peer       = ap_ctx_get(pCtx, "ssl::proxy::peer");
+    servername = ap_ctx_get(pCtx, "ssl::proxy::servername");
+    sc         = mySrvConfig(s);
+
+    /*
+     * Unless stated otherwise by the configuration, we really don't
+     * care if the verification was okay or not, so lets return now
+     * before we do anything involving memory or time.
+     */
+    if (sc->bProxyVerify == FALSE)
+        return ok;
+                     
+    /*
+     * Get verify ingredients
+     */
+    xs       = X509_STORE_CTX_get_current_cert(ctx);
+    errnum   = X509_STORE_CTX_get_error(ctx);
+    errdepth = X509_STORE_CTX_get_error_depth(ctx);
+
+    /* 
+     * Log verification information
+     */
+    cp  = X509_NAME_oneline(X509_get_subject_name(xs), NULL, 0);
+    cp2 = X509_NAME_oneline(X509_get_issuer_name(xs),  NULL, 0);
+    ssl_log(s, SSL_LOG_DEBUG,
+            "SSL Proxy: (%s) Certificate Verification for remote server %s: "
+            "depth: %d, subject: %s, issuer: %s", 
+            servername, peer != NULL ? peer : "-unknown-",
+            errdepth, cp != NULL ? cp : "-unknown-", 
+            cp2 != NULL ? cp2 : "-unknown");
+    free(cp);
+    free(cp2);
+
+    /*
+     * If we already know it's not ok, log the real reason
+     */
+    if (!ok) {
+        ssl_log(s, SSL_LOG_ERROR,
+                "SSL Proxy: (%s) Certificate Verification failed for %s: "
+                "Error (%d): %s", servername,
+                peer != NULL ? peer : "-unknown-",
+                errnum, X509_verify_cert_error_string(errnum));
+        ap_ctx_set(pCtx, "ssl::proxy::verifyerror", 
+                   (void *)X509_verify_cert_error_string(errnum));
+        return ok;
+    }
+
+    /*
+     * Check the depth of the certificate chain
+     */
+    if (sc->nProxyVerifyDepth > 0) {
+        if (errdepth > sc->nProxyVerifyDepth) {
+            ssl_log(s, SSL_LOG_ERROR,
+                "SSL Proxy: (%s) Certificate Verification failed for %s: "
+                "Certificate Chain too long "
+                "(chain has %d certificates, but maximum allowed are only %d)", 
+                servername, peer, errdepth, sc->nProxyVerifyDepth);
+            ap_ctx_set(pCtx, "ssl::proxy::verifyerror",
+                       (void *)X509_verify_cert_error_string(X509_V_ERR_CERT_CHAIN_TOO_LONG));
+            ok = FALSE;
+        }
+    }
+
+    /*
+     * And finally signal OpenSSL the (perhaps changed) state
+     */
+    return (ok);
+}
+
+#endif /* SSL_EXPERIMENTAL_PROXY */
+
+/*  _________________________________________________________________
+**
+**  SSL Extension to mod_status
+**  _________________________________________________________________
+*/
+
+static void ssl_ext_ms_display(request_rec *, int, int);
+
+static void ssl_ext_ms_register(void)
+{
+    ap_hook_register("ap::mod_status::display", ssl_ext_ms_display, AP_HOOK_NOCTX);
+    return;
+}
+
+static void ssl_ext_ms_unregister(void)
+{
+    ap_hook_unregister("ap::mod_status::display", ssl_ext_ms_display);
+    return;
+}
+
+static void ssl_ext_ms_display_cb(char *str, void *_r)
+{
+    request_rec *r = (request_rec *)_r;
+    if (str != NULL)
+        ap_rputs(str, r);
+    return;
+}
+
+static void ssl_ext_ms_display(request_rec *r, int no_table_report, int short_report)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(r->server);
+
+    if (sc == NULL)
+        return;
+    if (short_report)
+        return;
+    ap_rputs("<hr>\n", r);
+    ap_rputs("<table cellspacing=0 cellpadding=0>\n", r);
+    ap_rputs("<tr><td bgcolor=\"#000000\">\n", r);
+    ap_rputs("<b><font color=\"#ffffff\" face=\"Arial,Helvetica\">SSL/TLS Session Cache Status:</font></b>\r", r);
+    ap_rputs("</td></tr>\n", r);
+    ap_rputs("<tr><td bgcolor=\"#ffffff\">\n", r);
+    ssl_scache_status(r->server, r->pool, ssl_ext_ms_display_cb, r);
+    ap_rputs("</td></tr>\n", r);
+    ap_rputs("</table>\n", r);
+    return;
+}
+
diff --git a/modules/ssl/ssl_engine_init.c b/modules/ssl/ssl_engine_init.c
new file mode 100644 (file)
index 0000000..6a27c4a
--- /dev/null
@@ -0,0 +1,1090 @@
+/*                      _             _
+**  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
+** | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
+** | | | | | | (_) | (_| |   \__ \__ \ |  www.modssl.org
+** |_| |_| |_|\___/ \__,_|___|___/___/_|  ftp.modssl.org
+**                      |_____|
+**  ssl_engine_init.c
+**  Initialization of Servers
+*/
+
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ *    nor may "mod_ssl" appear in their names without prior
+ *    written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+/* ====================================================================
+ * Copyright (c) 1995-1999 Ben Laurie. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by Ben Laurie
+ *    for use in the Apache-SSL HTTP server project."
+ *
+ * 4. The name "Apache-SSL Server" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 5. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Ben Laurie
+ *    for use in the Apache-SSL HTTP server project."
+ *
+ * THIS SOFTWARE IS PROVIDED BY BEN LAURIE ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL BEN LAURIE OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+                             /* ``Recursive, adj.;
+                                  see Recursive.''
+                                        -- Unknown   */
+#include "mod_ssl.h"
+
+
+/*  _________________________________________________________________
+**
+**  Module Initialization
+**  _________________________________________________________________
+*/
+
+/*
+ *  Per-module initialization
+ */
+void ssl_init_Module(server_rec *s, pool *p)
+{
+    SSLModConfigRec *mc = myModConfig();
+    SSLSrvConfigRec *sc;
+    server_rec *s2;
+    char *cp;
+
+    mc->nInitCount++;
+
+    /*
+     * Let us cleanup on restarts and exists
+     */
+    ap_register_cleanup(p, s, ssl_init_ModuleKill, ssl_init_ChildKill);
+
+    /*
+     * Any init round fixes the global config
+     */
+    ssl_config_global_create(); /* just to avoid problems */
+    ssl_config_global_fix();
+
+    /*
+     *  try to fix the configuration and open the dedicated SSL
+     *  logfile as early as possible
+     */
+    for (s2 = s; s2 != NULL; s2 = s2->next) {
+        sc = mySrvConfig(s2);
+
+        /* Fix up stuff that may not have been set */
+        if (sc->bEnabled == UNSET)
+            sc->bEnabled = FALSE;
+        if (sc->nVerifyClient == SSL_CVERIFY_UNSET)
+            sc->nVerifyClient = SSL_CVERIFY_NONE;
+        if (sc->nVerifyDepth == UNSET)
+            sc->nVerifyDepth = 1;
+#ifdef SSL_EXPERIMENTAL_PROXY
+        if (sc->nProxyVerifyDepth == UNSET)
+            sc->nProxyVerifyDepth = 1;
+#endif
+        if (sc->nSessionCacheTimeout == UNSET)
+            sc->nSessionCacheTimeout = SSL_SESSION_CACHE_TIMEOUT;
+        if (sc->nPassPhraseDialogType == SSL_PPTYPE_UNSET)
+            sc->nPassPhraseDialogType = SSL_PPTYPE_BUILTIN;
+
+        /* Open the dedicated SSL logfile */
+        ssl_log_open(s, s2, p);
+    }
+
+    /*
+     * Identification
+     */
+    if (mc->nInitCount == 1) {
+        ssl_log(s, SSL_LOG_INFO, "Server: %s, Interface: %s, Library: %s",
+                SERVER_BASEVERSION,
+                ssl_var_lookup(p, NULL, NULL, NULL, "SSL_VERSION_INTERFACE"),
+                ssl_var_lookup(p, NULL, NULL, NULL, "SSL_VERSION_LIBRARY"));
+#ifdef WIN32
+        ssl_log(s, SSL_LOG_WARN, "You are using mod_ssl under Win32. " 
+                "This combination is *NOT* officially supported. "
+                "Use it at your own risk!");
+#endif
+    }
+
+    /*
+     * Initialization round information
+     */
+    if (mc->nInitCount == 1)
+        ssl_log(s, SSL_LOG_INFO, "Init: 1st startup round (still not detached)");
+    else if (mc->nInitCount == 2)
+        ssl_log(s, SSL_LOG_INFO, "Init: 2nd startup round (already detached)");
+    else
+        ssl_log(s, SSL_LOG_INFO, "Init: %d%s restart round (already detached)",
+                mc->nInitCount-2, (mc->nInitCount-2) == 1 ? "st" : "nd");
+
+#ifdef SSL_VENDOR
+    ap_hook_use("ap::mod_ssl::vendor::init_module",
+                AP_HOOK_SIG3(void,ptr,ptr), AP_HOOK_ALL, s, p);
+#endif
+
+    /*
+     *  The initialization phase inside the Apache API is totally bogus.
+     *  We actually have three non-trivial problems:
+     *
+     *  1. Under Unix the API does a 2-round initialization of modules while
+     *     under Win32 it doesn't. This means we have to make sure that at
+     *     least the pass phrase dialog doesn't occur twice.  We overcome this
+     *     problem by using a counter (mc->nInitCount) which has to
+     *     survive the init rounds.
+     *
+     *  2. Between the first and the second round Apache detaches from
+     *     the terminal under Unix. This means that our pass phrase dialog
+     *     _has_ to be done in the first round and _cannot_ be done in the
+     *     second round.
+     *
+     *  3. When Dynamic Shared Object (DSO) mechanism is used under Unix the
+     *     module segment (code & data) gets unloaded and re-loaded between
+     *     the first and the second round. This means no global data survives
+     *     between first and the second init round. We overcome this by using
+     *     an entry ("ssl_module") inside the ap_global_ctx.
+     *
+     *  The situation as a table:
+     *
+     *  Unix/static Unix/DSO          Win32     Action Required
+     *              (-DSHARED_MODULE) (-DWIN32)
+     *  ----------- ----------------- --------- -----------------------------------
+     *  -           load module       -         -
+     *  init        init              init      SSL library init, Pass Phrase Dialog
+     *  detach      detach            -         -
+     *  -           reload module     -         -
+     *  init        init              -         SSL library init, mod_ssl init
+     *
+     *  Ok, now try to solve this totally ugly situation...
+     */
+
+#ifdef SHARED_MODULE
+    ssl_log(s, SSL_LOG_INFO, "Init: %snitializing %s library",
+            mc->nInitCount == 1 ? "I" : "Rei", SSL_LIBRARY_NAME);
+    ssl_init_SSLLibrary();
+#else
+    if (mc->nInitCount <= 2) {
+        ssl_log(s, SSL_LOG_INFO, "Init: %snitializing %s library",
+                mc->nInitCount == 1 ? "I" : "Rei", SSL_LIBRARY_NAME);
+        ssl_init_SSLLibrary();
+    }
+#endif
+    if (mc->nInitCount == 1) {
+        ssl_pphrase_Handle(s, p);
+        ssl_init_TmpKeysHandle(SSL_TKP_GEN, s, p);
+#ifndef WIN32
+        return;
+#endif
+    }
+
+    /*
+     * SSL external crypto device ("engine") support
+     */
+#ifdef SSL_EXPERIMENTAL_ENGINE
+    ssl_init_Engine(s, p);
+#endif
+
+    /*
+     * Warn the user that he should use the session cache.
+     * But we can operate without it, of course.
+     */
+    if (mc->nSessionCacheMode == SSL_SCMODE_UNSET) {
+        ssl_log(s, SSL_LOG_WARN,
+                "Init: Session Cache is not configured [hint: SSLSessionCache]");
+        mc->nSessionCacheMode = SSL_SCMODE_NONE;
+    }
+
+    /*
+     *  initialize the mutex handling and session caching
+     */
+    ssl_mutex_init(s, p);
+    ssl_scache_init(s, p);
+
+    /*
+     * Seed the Pseudo Random Number Generator (PRNG)
+     */
+    ssl_rand_seed(s, p, SSL_RSCTX_STARTUP, "Init: ");
+
+    /*
+     *  allocate the temporary RSA keys and DH params
+     */
+    ssl_init_TmpKeysHandle(SSL_TKP_ALLOC, s, p);
+
+    /*
+     *  initialize servers
+     */
+    ssl_log(s, SSL_LOG_INFO, "Init: Initializing (virtual) servers for SSL");
+    for (s2 = s; s2 != NULL; s2 = s2->next) {
+        sc = mySrvConfig(s2);
+        /*
+         * Either now skip this server when SSL is disabled for
+         * it or give out some information about what we're
+         * configuring.
+         */
+        if (!sc->bEnabled)
+            continue;
+        ssl_log(s2, SSL_LOG_INFO,
+                "Init: Configuring server %s for SSL protocol",
+                ssl_util_vhostid(p, s2));
+
+        /*
+         * Read the server certificate and key
+         */
+        ssl_init_ConfigureServer(s2, p, sc);
+    }
+
+    /*
+     * Configuration consistency checks
+     */
+    ssl_init_CheckServers(s, p);
+
+    /*
+     *  Announce mod_ssl and SSL library in HTTP Server field
+     *  as ``mod_ssl/X.X.X OpenSSL/X.X.X''
+     */
+    if ((cp = ssl_var_lookup(p, NULL, NULL, NULL, "SSL_VERSION_PRODUCT")) != NULL && cp[0] != NUL)
+        ap_add_version_component(cp);
+    ap_add_version_component(ssl_var_lookup(p, NULL, NULL, NULL, "SSL_VERSION_INTERFACE"));
+    ap_add_version_component(ssl_var_lookup(p, NULL, NULL, NULL, "SSL_VERSION_LIBRARY"));
+
+    return;
+}
+
+/*
+ *  Initialize SSL library (also already needed for the pass phrase dialog)
+ */
+void ssl_init_SSLLibrary(void)
+{
+#ifdef WIN32
+    CRYPTO_malloc_init();
+#endif
+    SSL_load_error_strings();
+    SSL_library_init();
+    ssl_util_thread_setup();
+    X509V3_add_standard_extensions();
+    return;
+}
+
+/*
+ * Support for external a Crypto Device ("engine"), usually
+ * a hardware accellerator card for crypto operations.
+ */
+#ifdef SSL_EXPERIMENTAL_ENGINE
+void ssl_init_Engine(server_rec *s, pool *p)
+{
+    SSLModConfigRec *mc = myModConfig();
+    ENGINE *e;
+
+    if (mc->szCryptoDevice != NULL) {
+        if ((e = ENGINE_by_id(mc->szCryptoDevice)) == NULL) {
+            ssl_log(s, SSL_LOG_ERROR, "Init: Failed to load Crypto Device API `%s'",
+                    mc->szCryptoDevice);
+            ssl_die();
+        }
+        if (strEQ(mc->szCryptoDevice, "chil")) 
+            ENGINE_ctrl(e, ENGINE_CTRL_CHIL_SET_FORKCHECK, 1, 0, 0);
+        if (!ENGINE_set_default(e, ENGINE_METHOD_ALL)) {
+            ssl_log(s, SSL_LOG_ERROR, "Init: Failed to enable Crypto Device API `%s'",
+                    mc->szCryptoDevice);
+            ssl_die();
+        }
+        ENGINE_free(e);
+    }
+    return;
+}
+#endif
+
+/*
+ * Handle the Temporary RSA Keys and DH Params
+ */
+void ssl_init_TmpKeysHandle(int action, server_rec *s, pool *p)
+{
+    SSLModConfigRec *mc = myModConfig();
+    ssl_asn1_t *asn1;
+    unsigned char *ucp;
+    RSA *rsa;
+    DH *dh;
+
+    /* Generate Keys and Params */
+    if (action == SSL_TKP_GEN) {
+
+        /* seed PRNG */
+        ssl_rand_seed(s, p, SSL_RSCTX_STARTUP, "Init: ");
+
+        /* generate 512 bit RSA key */
+        ssl_log(s, SSL_LOG_INFO, "Init: Generating temporary RSA private keys (512/1024 bits)");
+        if ((rsa = RSA_generate_key(512, RSA_F4, NULL, NULL)) == NULL) {
+            ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR, 
+                    "Init: Failed to generate temporary 512 bit RSA private key");
+            ssl_die();
+        }
+        asn1 = (ssl_asn1_t *)ssl_ds_table_push(mc->tTmpKeys, "RSA:512");
+        asn1->nData  = i2d_RSAPrivateKey(rsa, NULL);
+        asn1->cpData = ap_palloc(mc->pPool, asn1->nData);
+        ucp = asn1->cpData; i2d_RSAPrivateKey(rsa, &ucp); /* 2nd arg increments */
+        RSA_free(rsa);
+
+        /* generate 1024 bit RSA key */
+        if ((rsa = RSA_generate_key(1024, RSA_F4, NULL, NULL)) == NULL) {
+            ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR, 
+                    "Init: Failed to generate temporary 1024 bit RSA private key");
+            ssl_die();
+        }
+        asn1 = (ssl_asn1_t *)ssl_ds_table_push(mc->tTmpKeys, "RSA:1024");
+        asn1->nData  = i2d_RSAPrivateKey(rsa, NULL);
+        asn1->cpData = ap_palloc(mc->pPool, asn1->nData);
+        ucp = asn1->cpData; i2d_RSAPrivateKey(rsa, &ucp); /* 2nd arg increments */
+        RSA_free(rsa);
+
+        ssl_log(s, SSL_LOG_INFO, "Init: Configuring temporary DH parameters (512/1024 bits)");
+
+        /* import 512 bit DH param */
+        if ((dh = ssl_dh_GetTmpParam(512)) == NULL) {
+            ssl_log(s, SSL_LOG_ERROR, "Init: Failed to import temporary 512 bit DH parameters");
+            ssl_die();
+        }
+        asn1 = (ssl_asn1_t *)ssl_ds_table_push(mc->tTmpKeys, "DH:512");
+        asn1->nData  = i2d_DHparams(dh, NULL);
+        asn1->cpData = ap_palloc(mc->pPool, asn1->nData);
+        ucp = asn1->cpData; i2d_DHparams(dh, &ucp); /* 2nd arg increments */
+        /* no need to free dh, it's static */
+
+        /* import 1024 bit DH param */
+        if ((dh = ssl_dh_GetTmpParam(1024)) == NULL) {
+            ssl_log(s, SSL_LOG_ERROR, "Init: Failed to import temporary 1024 bit DH parameters");
+            ssl_die();
+        }
+        asn1 = (ssl_asn1_t *)ssl_ds_table_push(mc->tTmpKeys, "DH:1024");
+        asn1->nData  = i2d_DHparams(dh, NULL);
+        asn1->cpData = ap_palloc(mc->pPool, asn1->nData);
+        ucp = asn1->cpData; i2d_DHparams(dh, &ucp); /* 2nd arg increments */
+        /* no need to free dh, it's static */
+    }
+
+    /* Allocate Keys and Params */
+    else if (action == SSL_TKP_ALLOC) {
+
+        ssl_log(s, SSL_LOG_INFO, "Init: Configuring temporary RSA private keys (512/1024 bits)");
+
+        /* allocate 512 bit RSA key */
+        if ((asn1 = (ssl_asn1_t *)ssl_ds_table_get(mc->tTmpKeys, "RSA:512")) != NULL) {
+            ucp = asn1->cpData;
+            if ((mc->pTmpKeys[SSL_TKPIDX_RSA512] = 
+#if SSL_LIBRARY_VERSION >= 0x00907000
+                 (void *)d2i_RSAPrivateKey(NULL, (const unsigned char **)&ucp, asn1->nData)) == NULL) {
+#else
+                 (void *)d2i_RSAPrivateKey(NULL, &ucp, asn1->nData)) == NULL) {
+#endif
+                ssl_log(s, SSL_LOG_ERROR, "Init: Failed to load temporary 512 bit RSA private key");
+                ssl_die();
+            }
+        }
+
+        /* allocate 1024 bit RSA key */
+        if ((asn1 = (ssl_asn1_t *)ssl_ds_table_get(mc->tTmpKeys, "RSA:1024")) != NULL) {
+            ucp = asn1->cpData;
+            if ((mc->pTmpKeys[SSL_TKPIDX_RSA1024] = 
+#if SSL_LIBRARY_VERSION >= 0x00907000
+                 (void *)d2i_RSAPrivateKey(NULL, (const unsigned char **)&ucp, asn1->nData)) == NULL) {
+#else
+                 (void *)d2i_RSAPrivateKey(NULL, &ucp, asn1->nData)) == NULL) {
+#endif
+                ssl_log(s, SSL_LOG_ERROR, "Init: Failed to load temporary 1024 bit RSA private key");
+                ssl_die();
+            }
+        }
+
+        ssl_log(s, SSL_LOG_INFO, "Init: Configuring temporary DH parameters (512/1024 bits)");
+
+        /* allocate 512 bit DH param */
+        if ((asn1 = (ssl_asn1_t *)ssl_ds_table_get(mc->tTmpKeys, "DH:512")) != NULL) {
+            ucp = asn1->cpData;
+            if ((mc->pTmpKeys[SSL_TKPIDX_DH512] = 
+#if SSL_LIBRARY_VERSION >= 0x00907000
+                 (void *)d2i_DHparams(NULL, (const unsigned char **)&ucp, asn1->nData)) == NULL) {
+#else
+                 (void *)d2i_DHparams(NULL, &ucp, asn1->nData)) == NULL) {
+#endif
+                ssl_log(s, SSL_LOG_ERROR, "Init: Failed to load temporary 512 bit DH parameters");
+                ssl_die();
+            }
+        }
+
+        /* allocate 1024 bit DH param */
+        if ((asn1 = (ssl_asn1_t *)ssl_ds_table_get(mc->tTmpKeys, "DH:1024")) != NULL) {
+            ucp = asn1->cpData;
+            if ((mc->pTmpKeys[SSL_TKPIDX_DH1024] = 
+#if SSL_LIBRARY_VERSION >= 0x00907000
+                 (void *)d2i_DHparams(NULL, (const unsigned char **)&ucp, asn1->nData)) == NULL) {
+#else
+                 (void *)d2i_DHparams(NULL, &ucp, asn1->nData)) == NULL) {
+#endif
+                ssl_log(s, SSL_LOG_ERROR, "Init: Failed to load temporary 1024 bit DH parameters");
+                ssl_die();
+            }
+        }
+    }
+
+    /* Free Keys and Params */
+    else if (action == SSL_TKP_FREE) {
+        if (mc->pTmpKeys[SSL_TKPIDX_RSA512] != NULL) {
+            RSA_free((RSA *)mc->pTmpKeys[SSL_TKPIDX_RSA512]);
+            mc->pTmpKeys[SSL_TKPIDX_RSA512] = NULL;
+        }
+        if (mc->pTmpKeys[SSL_TKPIDX_RSA1024] != NULL) {
+            RSA_free((RSA *)mc->pTmpKeys[SSL_TKPIDX_RSA1024]);
+            mc->pTmpKeys[SSL_TKPIDX_RSA1024] = NULL;
+        }
+        if (mc->pTmpKeys[SSL_TKPIDX_DH512] != NULL) {
+            DH_free((DH *)mc->pTmpKeys[SSL_TKPIDX_DH512]);
+            mc->pTmpKeys[SSL_TKPIDX_DH512] = NULL;
+        }
+        if (mc->pTmpKeys[SSL_TKPIDX_DH1024] != NULL) {
+            DH_free((DH *)mc->pTmpKeys[SSL_TKPIDX_DH1024]);
+            mc->pTmpKeys[SSL_TKPIDX_DH1024] = NULL;
+        }
+    }
+    return;
+}
+
+/*
+ * Configure a particular server
+ */
+void ssl_init_ConfigureServer(server_rec *s, pool *p, SSLSrvConfigRec *sc)
+{
+    SSLModConfigRec *mc = myModConfig();
+    int nVerify;
+    char *cpVHostID;
+    EVP_PKEY *pKey;
+    SSL_CTX *ctx;
+    STACK_OF(X509_NAME) *skCAList;
+    ssl_asn1_t *asn1;
+    unsigned char *ucp;
+    char *cp;
+    BOOL ok;
+    BOOL bSkipFirst;
+    int isca, pathlen;
+    int i, n;
+
+    /*
+     * Create the server host:port string because we need it a lot
+     */
+    cpVHostID = ssl_util_vhostid(p, s);
+
+    /*
+     * Now check for important parameters and the
+     * possibility that the user forgot to set them.
+     */
+    if (sc->szPublicCertFile[0] == NULL) {
+        ssl_log(s, SSL_LOG_ERROR,
+                "Init: (%s) No SSL Certificate set [hint: SSLCertificateFile]",
+                cpVHostID);
+        ssl_die();
+    }
+
+    /*
+     *  Check for problematic re-initializations
+     */
+    if (sc->pPublicCert[SSL_AIDX_RSA] != NULL ||
+        sc->pPublicCert[SSL_AIDX_DSA] != NULL   ) {
+        ssl_log(s, SSL_LOG_ERROR,
+                "Init: (%s) Illegal attempt to re-initialise SSL for server "
+                "(theoretically shouldn't happen!)", cpVHostID);
+        ssl_die();
+    }
+
+    /*
+     *  Create the new per-server SSL context
+     */
+    if (sc->nProtocol == SSL_PROTOCOL_NONE) {
+        ssl_log(s, SSL_LOG_ERROR,
+                "Init: (%s) No SSL protocols available [hint: SSLProtocol]",
+                cpVHostID);
+        ssl_die();
+    }
+    cp = ap_pstrcat(p, (sc->nProtocol & SSL_PROTOCOL_SSLV2 ? "SSLv2, " : ""),
+                       (sc->nProtocol & SSL_PROTOCOL_SSLV3 ? "SSLv3, " : ""),
+                       (sc->nProtocol & SSL_PROTOCOL_TLSV1 ? "TLSv1, " : ""), NULL);
+    cp[strlen(cp)-2] = NUL;
+    ssl_log(s, SSL_LOG_TRACE,
+            "Init: (%s) Creating new SSL context (protocols: %s)", cpVHostID, cp);
+    if (sc->nProtocol == SSL_PROTOCOL_SSLV2)
+        ctx = SSL_CTX_new(SSLv2_server_method());  /* only SSLv2 is left */
+    else
+        ctx = SSL_CTX_new(SSLv23_server_method()); /* be more flexible */
+    SSL_CTX_set_options(ctx, SSL_OP_ALL);
+    if (!(sc->nProtocol & SSL_PROTOCOL_SSLV2))
+        SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2);
+    if (!(sc->nProtocol & SSL_PROTOCOL_SSLV3))
+        SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3);
+    if (!(sc->nProtocol & SSL_PROTOCOL_TLSV1))
+        SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1);
+    SSL_CTX_set_app_data(ctx, s);
+    sc->pSSLCtx = ctx;
+
+    /*
+     * Configure additional context ingredients
+     */
+    SSL_CTX_set_options(ctx, SSL_OP_SINGLE_DH_USE);
+    if (mc->nSessionCacheMode == SSL_SCMODE_NONE)
+        SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
+    else
+        SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_SERVER);
+
+    /*
+     *  Configure callbacks for SSL context
+     */
+    nVerify = SSL_VERIFY_NONE;
+    if (sc->nVerifyClient == SSL_CVERIFY_REQUIRE)
+        nVerify |= SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
+    if (   (sc->nVerifyClient == SSL_CVERIFY_OPTIONAL)
+        || (sc->nVerifyClient == SSL_CVERIFY_OPTIONAL_NO_CA) )
+        nVerify |= SSL_VERIFY_PEER;
+    SSL_CTX_set_verify(ctx, nVerify,  ssl_callback_SSLVerify);
+    SSL_CTX_sess_set_new_cb(ctx,      ssl_callback_NewSessionCacheEntry);
+    SSL_CTX_sess_set_get_cb(ctx,      ssl_callback_GetSessionCacheEntry);
+    SSL_CTX_sess_set_remove_cb(ctx,   ssl_callback_DelSessionCacheEntry);
+    SSL_CTX_set_tmp_rsa_callback(ctx, ssl_callback_TmpRSA);
+    SSL_CTX_set_tmp_dh_callback(ctx,  ssl_callback_TmpDH);
+    SSL_CTX_set_info_callback(ctx,    ssl_callback_LogTracingState);
+
+    /*
+     *  Configure SSL Cipher Suite
+     */
+    if (sc->szCipherSuite != NULL) {
+        ssl_log(s, SSL_LOG_TRACE,
+                "Init: (%s) Configuring permitted SSL ciphers [%s]", 
+                cpVHostID, sc->szCipherSuite);
+        if (!SSL_CTX_set_cipher_list(ctx, sc->szCipherSuite)) {
+            ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+                    "Init: (%s) Unable to configure permitted SSL ciphers",
+                    cpVHostID);
+            ssl_die();
+        }
+    }
+
+    /*
+     * Configure Client Authentication details
+     */
+    if (sc->szCACertificateFile != NULL || sc->szCACertificatePath != NULL) {
+        ssl_log(s, SSL_LOG_TRACE,
+                "Init: (%s) Configuring client authentication", cpVHostID);
+        if (!SSL_CTX_load_verify_locations(ctx,
+                                           sc->szCACertificateFile,
+                                           sc->szCACertificatePath)) {
+            ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+                    "Init: (%s) Unable to configure verify locations "
+                    "for client authentication", cpVHostID);
+            ssl_die();
+        }
+        if ((skCAList = ssl_init_FindCAList(s, p, sc->szCACertificateFile,
+                                            sc->szCACertificatePath)) == NULL) {
+            ssl_log(s, SSL_LOG_ERROR,
+                    "Init: (%s) Unable to determine list of available "
+                    "CA certificates for client authentication", cpVHostID);
+            ssl_die();
+        }
+        SSL_CTX_set_client_CA_list(sc->pSSLCtx, skCAList);
+    }
+
+    /*
+     * Configure Certificate Revocation List (CRL) Details
+     */
+    if (sc->szCARevocationFile != NULL || sc->szCARevocationPath != NULL) {
+        ssl_log(s, SSL_LOG_TRACE,
+                "Init: (%s) Configuring certificate revocation facility", cpVHostID);
+        if ((sc->pRevocationStore =
+                SSL_X509_STORE_create(sc->szCARevocationFile,
+                                      sc->szCARevocationPath)) == NULL) {
+            ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+                    "Init: (%s) Unable to configure X.509 CRL storage "
+                    "for certificate revocation", cpVHostID);
+            ssl_die();
+        }
+    }
+
+    /*
+     * Give a warning when no CAs were configured but client authentication
+     * should take place. This cannot work.
+     */
+    if (sc->nVerifyClient == SSL_CVERIFY_REQUIRE) {
+        skCAList = SSL_CTX_get_client_CA_list(ctx);
+        if (sk_X509_NAME_num(skCAList) == 0)
+            ssl_log(s, SSL_LOG_WARN,
+                    "Init: Ops, you want to request client authentication, "
+                    "but no CAs are known for verification!? "
+                    "[Hint: SSLCACertificate*]");
+    }
+
+    /*
+     *  Configure server certificate(s)
+     */
+    ok = FALSE;
+    cp = ap_psprintf(p, "%s:RSA", cpVHostID);
+    if ((asn1 = (ssl_asn1_t *)ssl_ds_table_get(mc->tPublicCert, cp)) != NULL) {
+        ssl_log(s, SSL_LOG_TRACE,
+                "Init: (%s) Configuring RSA server certificate", cpVHostID);
+        ucp = asn1->cpData;
+        if ((sc->pPublicCert[SSL_AIDX_RSA] = d2i_X509(NULL, &ucp, asn1->nData)) == NULL) {
+            ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+                    "Init: (%s) Unable to import RSA server certificate",
+                    cpVHostID);
+            ssl_die();
+        }
+        if (SSL_CTX_use_certificate(ctx, sc->pPublicCert[SSL_AIDX_RSA]) <= 0) {
+            ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+                    "Init: (%s) Unable to configure RSA server certificate",
+                    cpVHostID);
+            ssl_die();
+        }
+        ok = TRUE;
+    }
+    cp = ap_psprintf(p, "%s:DSA", cpVHostID);
+    if ((asn1 = (ssl_asn1_t *)ssl_ds_table_get(mc->tPublicCert, cp)) != NULL) {
+        ssl_log(s, SSL_LOG_TRACE,
+                "Init: (%s) Configuring DSA server certificate", cpVHostID);
+        ucp = asn1->cpData;
+        if ((sc->pPublicCert[SSL_AIDX_DSA] = d2i_X509(NULL, &ucp, asn1->nData)) == NULL) {
+            ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+                    "Init: (%s) Unable to import DSA server certificate",
+                    cpVHostID);
+            ssl_die();
+        }
+        if (SSL_CTX_use_certificate(ctx, sc->pPublicCert[SSL_AIDX_DSA]) <= 0) {
+            ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+                    "Init: (%s) Unable to configure DSA server certificate",
+                    cpVHostID);
+            ssl_die();
+        }
+        ok = TRUE;
+    }
+    if (!ok) {
+        ssl_log(s, SSL_LOG_ERROR,
+                "Init: (%s) Ops, no RSA or DSA server certificate found?!", cpVHostID);
+        ssl_log(s, SSL_LOG_ERROR,
+                "Init: (%s) You have to perform a *full* server restart when you added or removed a certificate and/or key file", cpVHostID);
+        ssl_die();
+    }
+
+    /*
+     * Some information about the certificate(s)
+     */
+    for (i = 0; i < SSL_AIDX_MAX; i++) {
+        if (sc->pPublicCert[i] != NULL) {
+            if (SSL_X509_isSGC(sc->pPublicCert[i])) {
+                ssl_log(s, SSL_LOG_INFO,
+                        "Init: (%s) %s server certificate enables "
+                        "Server Gated Cryptography (SGC)", 
+                        cpVHostID, (i == SSL_AIDX_RSA ? "RSA" : "DSA"));
+            }
+            if (SSL_X509_getBC(sc->pPublicCert[i], &isca, &pathlen)) {
+                if (isca)
+                    ssl_log(s, SSL_LOG_WARN,
+                        "Init: (%s) %s server certificate is a CA certificate "
+                        "(BasicConstraints: CA == TRUE !?)",
+                        cpVHostID, (i == SSL_AIDX_RSA ? "RSA" : "DSA"));
+                if (pathlen > 0)
+                    ssl_log(s, SSL_LOG_WARN,
+                        "Init: (%s) %s server certificate is not a leaf certificate "
+                        "(BasicConstraints: pathlen == %d > 0 !?)",
+                        cpVHostID, (i == SSL_AIDX_RSA ? "RSA" : "DSA"), pathlen);
+            }
+            if (SSL_X509_getCN(p, sc->pPublicCert[i], &cp)) {
+                if (ap_is_fnmatch(cp) &&
+                    !ap_fnmatch(cp, s->server_hostname, FNM_PERIOD|FNM_CASE_BLIND)) {
+                    ssl_log(s, SSL_LOG_WARN,
+                        "Init: (%s) %s server certificate wildcard CommonName (CN) `%s' "
+                        "does NOT match server name!?", cpVHostID, 
+                        (i == SSL_AIDX_RSA ? "RSA" : "DSA"), cp);
+                }
+                else if (strNE(s->server_hostname, cp)) {
+                    ssl_log(s, SSL_LOG_WARN,
+                        "Init: (%s) %s server certificate CommonName (CN) `%s' "
+                        "does NOT match server name!?", cpVHostID, 
+                        (i == SSL_AIDX_RSA ? "RSA" : "DSA"), cp);
+                }
+            }
+        }
+    }
+
+    /*
+     *  Configure server private key(s)
+     */
+    ok = FALSE;
+    cp = ap_psprintf(p, "%s:RSA", cpVHostID);
+    if ((asn1 = (ssl_asn1_t *)ssl_ds_table_get(mc->tPrivateKey, cp)) != NULL) {
+        ssl_log(s, SSL_LOG_TRACE,
+                "Init: (%s) Configuring RSA server private key", cpVHostID);
+        ucp = asn1->cpData;
+        if ((sc->pPrivateKey[SSL_AIDX_RSA] = 
+             d2i_PrivateKey(EVP_PKEY_RSA, NULL, &ucp, asn1->nData)) == NULL) {
+            ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+                    "Init: (%s) Unable to import RSA server private key",
+                    cpVHostID);
+            ssl_die();
+        }
+        if (SSL_CTX_use_PrivateKey(ctx, sc->pPrivateKey[SSL_AIDX_RSA]) <= 0) {
+            ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+                    "Init: (%s) Unable to configure RSA server private key",
+                    cpVHostID);
+            ssl_die();
+        }
+        ok = TRUE;
+    }
+    cp = ap_psprintf(p, "%s:DSA", cpVHostID);
+    if ((asn1 = (ssl_asn1_t *)ssl_ds_table_get(mc->tPrivateKey, cp)) != NULL) {
+        ssl_log(s, SSL_LOG_TRACE,
+                "Init: (%s) Configuring DSA server private key", cpVHostID);
+        ucp = asn1->cpData;
+        if ((sc->pPrivateKey[SSL_AIDX_DSA] = 
+             d2i_PrivateKey(EVP_PKEY_DSA, NULL, &ucp, asn1->nData)) == NULL) {
+            ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+                    "Init: (%s) Unable to import DSA server private key",
+                    cpVHostID);
+            ssl_die();
+        }
+        if (SSL_CTX_use_PrivateKey(ctx, sc->pPrivateKey[SSL_AIDX_DSA]) <= 0) {
+            ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+                    "Init: (%s) Unable to configure DSA server private key",
+                    cpVHostID);
+            ssl_die();
+        }
+        ok = TRUE;
+    }
+    if (!ok) {
+        ssl_log(s, SSL_LOG_ERROR,
+                "Init: (%s) Ops, no RSA or DSA server private key found?!", cpVHostID);
+        ssl_die();
+    }
+
+    /*
+     * Optionally copy DSA parameters for certificate from private key
+     * (see http://www.psy.uq.edu.au/~ftp/Crypto/ssleay/TODO.html)
+     */
+    if (   sc->pPublicCert[SSL_AIDX_DSA] != NULL
+        && sc->pPrivateKey[SSL_AIDX_DSA] != NULL) {
+        pKey = X509_get_pubkey(sc->pPublicCert[SSL_AIDX_DSA]);
+        if (   pKey != NULL
+            && EVP_PKEY_type(pKey->type) == EVP_PKEY_DSA 
+            && EVP_PKEY_missing_parameters(pKey))
+            EVP_PKEY_copy_parameters(pKey, sc->pPrivateKey[SSL_AIDX_DSA]);
+    }
+
+    /* 
+     * Optionally configure extra server certificate chain certificates.
+     * This is usually done by OpenSSL automatically when one of the
+     * server cert issuers are found under SSLCACertificatePath or in
+     * SSLCACertificateFile. But because these are intended for client
+     * authentication it can conflict. For instance when you use a
+     * Global ID server certificate you've to send out the intermediate
+     * CA certificate, too. When you would just configure this with
+     * SSLCACertificateFile and also use client authentication mod_ssl
+     * would accept all clients also issued by this CA. Obviously this
+     * isn't what we want in this situation. So this feature here exists
+     * to allow one to explicity configure CA certificates which are
+     * used only for the server certificate chain.
+     */
+    if (sc->szCertificateChain != NULL) {
+        bSkipFirst = FALSE;
+        for (i = 0; i < SSL_AIDX_MAX && sc->szPublicCertFile[i] != NULL; i++) {
+            if (strEQ(sc->szPublicCertFile[i], sc->szCertificateChain)) {
+                bSkipFirst = TRUE;
+                break;
+            }
+        }
+        if ((n = SSL_CTX_use_certificate_chain(ctx, sc->szCertificateChain, 
+                                               bSkipFirst, NULL)) < 0) {
+            ssl_log(s, SSL_LOG_ERROR,
+                    "Init: (%s) Failed to configure CA certificate chain!", cpVHostID);
+            ssl_die();
+        }
+        ssl_log(s, SSL_LOG_TRACE, "Init: (%s) Configuring "
+                "server certificate chain (%d CA certificate%s)", cpVHostID,
+                n, n == 1 ? "" : "s");
+    }
+
+#ifdef SSL_VENDOR
+    ap_hook_use("ap::mod_ssl::vendor::configure_server",
+                AP_HOOK_SIG4(void,ptr,ptr,ptr), AP_HOOK_ALL, 
+                s, p, sc);
+#endif
+
+    return;
+}
+
+void ssl_init_CheckServers(server_rec *sm, pool *p)
+{
+    server_rec *s;
+    server_rec **ps;
+    SSLSrvConfigRec *sc;
+    ssl_ds_table *t;
+    pool *sp;
+    char *key;
+    BOOL bConflict;
+
+    /*
+     * Give out warnings when a server has HTTPS configured 
+     * for the HTTP port or vice versa
+     */
+    for (s = sm; s != NULL; s = s->next) {
+        sc = mySrvConfig(s);
+        if (sc->bEnabled && s->port == DEFAULT_HTTP_PORT)
+            ssl_log(sm, SSL_LOG_WARN,
+                    "Init: (%s) You configured HTTPS(%d) on the standard HTTP(%d) port!",
+                    ssl_util_vhostid(p, s), DEFAULT_HTTPS_PORT, DEFAULT_HTTP_PORT);
+        if (!sc->bEnabled && s->port == DEFAULT_HTTPS_PORT)
+            ssl_log(sm, SSL_LOG_WARN,
+                    "Init: (%s) You configured HTTP(%d) on the standard HTTPS(%d) port!",
+                    ssl_util_vhostid(p, s), DEFAULT_HTTP_PORT, DEFAULT_HTTPS_PORT);
+    }
+
+    /*
+     * Give out warnings when more than one SSL-aware virtual server uses the
+     * same IP:port. This doesn't work because mod_ssl then will always use
+     * just the certificate/keys of one virtual host (which one cannot be said
+     * easily - but that doesn't matter here).
+     */
+    sp = ap_make_sub_pool(p);
+    t = ssl_ds_table_make(sp, sizeof(server_rec *));
+    bConflict = FALSE;
+    for (s = sm; s != NULL; s = s->next) {
+        sc = mySrvConfig(s);
+        if (!sc->bEnabled)
+            continue;
+        key = ap_psprintf(sp, "%pA:%u", &s->addrs->host_addr, s->addrs->host_port);
+        ps = ssl_ds_table_get(t, key);
+        if (ps != NULL) {
+            ssl_log(sm, SSL_LOG_WARN,
+                    "Init: SSL server IP/port conflict: %s (%s:%d) vs. %s (%s:%d)",
+                    ssl_util_vhostid(p, s), 
+                    (s->defn_name != NULL ? s->defn_name : "unknown"),
+                    s->defn_line_number,
+                    ssl_util_vhostid(p, *ps),
+                    ((*ps)->defn_name != NULL ? (*ps)->defn_name : "unknown"), 
+                    (*ps)->defn_line_number);
+            bConflict = TRUE;
+            continue;
+        }
+        ps = ssl_ds_table_push(t, key);
+        *ps = s;
+    }
+    ssl_ds_table_kill(t);
+    ap_destroy_pool(sp);
+    if (bConflict)
+        ssl_log(sm, SSL_LOG_WARN,
+                "Init: You should not use name-based virtual hosts in conjunction with SSL!!");
+
+    return;
+}
+
+static int ssl_init_FindCAList_X509NameCmp(X509_NAME **a, X509_NAME **b)
+{
+    return(X509_NAME_cmp(*a, *b));
+}
+
+STACK_OF(X509_NAME) *ssl_init_FindCAList(server_rec *s, pool *pp, char *cpCAfile, char *cpCApath)
+{
+    STACK_OF(X509_NAME) *skCAList;
+    STACK_OF(X509_NAME) *sk;
+    DIR *dir;
+    struct DIR_TYPE *direntry;
+    char *cp;
+    pool *p;
+    int n;
+
+    /*
+     * Use a subpool so we don't bloat up the server pool which
+     * is remains in memory for the complete operation time of
+     * the server.
+     */
+    p = ap_make_sub_pool(pp);
+
+    /*
+     * Start with a empty stack/list where new
+     * entries get added in sorted order.
+     */
+    skCAList = sk_X509_NAME_new(ssl_init_FindCAList_X509NameCmp);
+
+    /*
+     * Process CA certificate bundle file
+     */
+    if (cpCAfile != NULL) {
+        sk = SSL_load_client_CA_file(cpCAfile);
+        for(n = 0; sk != NULL && n < sk_X509_NAME_num(sk); n++) {
+            ssl_log(s, SSL_LOG_TRACE,
+                    "CA certificate: %s",
+                    X509_NAME_oneline(sk_X509_NAME_value(sk, n), NULL, 0));
+            if (sk_X509_NAME_find(skCAList, sk_X509_NAME_value(sk, n)) < 0)
+                sk_X509_NAME_push(skCAList, sk_X509_NAME_value(sk, n));
+        }
+    }
+
+    /*
+     * Process CA certificate path files
+     */
+    if (cpCApath != NULL) {
+        dir = ap_popendir(p, cpCApath);
+        while ((direntry = readdir(dir)) != NULL) {
+            cp = ap_pstrcat(p, cpCApath, "/", direntry->d_name, NULL);
+            sk = SSL_load_client_CA_file(cp);
+            for(n = 0; sk != NULL && n < sk_X509_NAME_num(sk); n++) {
+                ssl_log(s, SSL_LOG_TRACE,
+                        "CA certificate: %s",
+                        X509_NAME_oneline(sk_X509_NAME_value(sk, n), NULL, 0));
+                if (sk_X509_NAME_find(skCAList, sk_X509_NAME_value(sk, n)) < 0)
+                    sk_X509_NAME_push(skCAList, sk_X509_NAME_value(sk, n));
+            }
+        }
+        ap_pclosedir(p, dir);
+    }
+
+    /*
+     * Cleanup
+     */
+    sk_X509_NAME_set_cmp_func(skCAList, NULL);
+    ap_destroy_pool(p);
+
+    return skCAList;
+}
+
+void ssl_init_Child(server_rec *s, pool *p)
+{
+     /* open the mutex lockfile */
+     ssl_mutex_reinit(s, p);
+     return;
+}
+
+void ssl_init_ChildKill(void *data)
+{
+    /* currently nothing to do */
+    return;
+}
+
+void ssl_init_ModuleKill(void *data)
+{
+    SSLSrvConfigRec *sc;
+    server_rec *s = (server_rec *)data;
+
+    /*
+     * Drop the session cache and mutex
+     */
+    ssl_scache_kill(s);
+    ssl_mutex_kill(s);
+
+    /* 
+     * Destroy the temporary keys and params
+     */
+    ssl_init_TmpKeysHandle(SSL_TKP_FREE, s, NULL);
+
+    /*
+     * Free the non-pool allocated structures
+     * in the per-server configurations
+     */
+    for (; s != NULL; s = s->next) {
+        sc = mySrvConfig(s);
+        if (sc->pPublicCert[SSL_AIDX_RSA] != NULL) {
+            X509_free(sc->pPublicCert[SSL_AIDX_RSA]);
+            sc->pPublicCert[SSL_AIDX_RSA] = NULL;
+        }
+        if (sc->pPublicCert[SSL_AIDX_DSA] != NULL) {
+            X509_free(sc->pPublicCert[SSL_AIDX_DSA]);
+            sc->pPublicCert[SSL_AIDX_DSA] = NULL;
+        }
+        if (sc->pPrivateKey[SSL_AIDX_RSA] != NULL) {
+            EVP_PKEY_free(sc->pPrivateKey[SSL_AIDX_RSA]);
+            sc->pPrivateKey[SSL_AIDX_RSA] = NULL;
+        }
+        if (sc->pPrivateKey[SSL_AIDX_DSA] != NULL) {
+            EVP_PKEY_free(sc->pPrivateKey[SSL_AIDX_DSA]);
+            sc->pPrivateKey[SSL_AIDX_DSA] = NULL;
+        }
+        if (sc->pSSLCtx != NULL) {
+            SSL_CTX_free(sc->pSSLCtx);
+            sc->pSSLCtx = NULL;
+        }
+    }
+
+    /*
+     * Try to kill the internals of the SSL library.
+     */
+#ifdef SHARED_MODULE
+    ERR_free_strings();
+    ERR_remove_state(0);
+    EVP_cleanup();
+#endif
+
+    return;
+}
+
diff --git a/modules/ssl/ssl_engine_io.c b/modules/ssl/ssl_engine_io.c
new file mode 100644 (file)
index 0000000..4ba1574
--- /dev/null
@@ -0,0 +1,728 @@
+/*                      _             _
+**  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
+** | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
+** | | | | | | (_) | (_| |   \__ \__ \ |  www.modssl.org
+** |_| |_| |_|\___/ \__,_|___|___/___/_|  ftp.modssl.org
+**                      |_____|
+**  ssl_engine_io.c
+**  I/O Functions
+*/
+
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ *    nor may "mod_ssl" appear in their names without prior
+ *    written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+                             /* ``MY HACK: This universe.
+                                  Just one little problem:
+                                  core keeps dumping.''
+                                            -- Unknown    */
+#include "mod_ssl.h"
+
+/*  _________________________________________________________________
+**
+**  I/O Request Body Sucking and Re-Injection
+**  _________________________________________________________________
+*/
+
+#ifndef SSL_CONSERVATIVE
+
+/*
+ * Background:
+ *
+ * 1. When the client sends a HTTP/HTTPS request, Apache's core code
+ * reads only the request line ("METHOD /path HTTP/x.y") and the
+ * attached MIME headers ("Foo: bar") up to the terminating line ("CR
+ * LF"). An attached request body (for instance the data of a POST
+ * method) is _NOT_ read. Instead it is read by mod_cgi's content
+ * handler and directly passed to the CGI script.
+ *
+ * 2. mod_ssl supports per-directory re-configuration of SSL parameters.
+ * This is implemented by performing an SSL renegotiation of the
+ * re-configured parameters after the request is read, but before the
+ * response is sent. In more detail: the renegotiation happens after the
+ * request line and MIME headers were read, but _before_ the attached
+ * request body is read. The reason simply is that in the HTTP protocol
+ * usually there is no acknowledgment step between the headers and the
+ * body (there is the 100-continue feature and the chunking facility
+ * only), so Apache has no API hook for this step.
+ *
+ * 3. the problem now occurs when the client sends a POST request for
+ * URL /foo via HTTPS the server and the server has SSL parameters
+ * re-configured on a per-URL basis for /foo. Then mod_ssl has to
+ * perform an SSL renegotiation after the request was read and before
+ * the response is sent. But the problem is the pending POST body data
+ * in the receive buffer of SSL (which Apache still has not read - it's
+ * pending until mod_cgi sucks it in). When mod_ssl now tries to perform
+ * the renegotiation the pending data leads to an I/O error.
+ *
+ * Solution Idea:
+ *
+ * There are only two solutions: Either to simply state that POST
+ * requests to URLs with SSL re-configurations are not allowed, or to
+ * renegotiate really after the _complete_ request (i.e. including
+ * the POST body) was read. Obviously the latter would be preferred,
+ * but it cannot be done easily inside Apache, because as already
+ * mentioned, there is no API step between the body reading and the body
+ * processing. And even when we mod_ssl would hook directly into the
+ * loop of mod_cgi, we wouldn't solve the problem for other handlers, of
+ * course. So the only general solution is to suck in the pending data
+ * of the request body from the OpenSSL BIO into the Apache BUFF. Then
+ * the renegotiation can be done and after this step Apache can proceed
+ * processing the request as before.
+ *
+ * Solution Implementation:
+ *
+ * We cannot simply suck in the data via an SSL_read-based loop because of
+ * HTTP chunking. Instead we _have_ to use the Apache API for this step which
+ * is aware of HTTP chunking. So the trick is to suck in the pending request
+ * data via the Apache API (which uses Apache's BUFF code and in the
+ * background mod_ssl's I/O glue code) and re-inject it later into the Apache
+ * BUFF code again. This way the data flows twice through the Apache BUFF, of
+ * course. But this way the solution doesn't depend on any Apache specifics
+ * and is fully transparent to Apache modules.
+ */
+
+struct ssl_io_suck_st {
+    BOOL  active;
+    char *bufptr;
+    int   buflen;
+    char *pendptr;
+    int   pendlen;
+};
+
+/* prepare request_rec structure for input sucking */
+static void ssl_io_suck_start(request_rec *r)
+{
+    struct ssl_io_suck_st *ss;
+
+    ss = ap_ctx_get(r->ctx, "ssl::io::suck");
+    if (ss == NULL) {
+        ss = ap_palloc(r->pool, sizeof(struct ssl_io_suck_st));
+        ap_ctx_set(r->ctx, "ssl::io::suck", ss);
+        ss->buflen  = 8192;
+        ss->bufptr  = ap_palloc(r->pool, ss->buflen);
+    }
+    ss->pendptr = ss->bufptr;
+    ss->pendlen = 0;
+    ss->active = FALSE;
+    return;
+}
+
+/* record a sucked input chunk */
+static void ssl_io_suck_record(request_rec *r, char *buf, int len)
+{
+    struct ssl_io_suck_st *ss;
+    
+    if ((ss = ap_ctx_get(r->ctx, "ssl::io::suck")) == NULL)
+        return;
+    if (((ss->bufptr + ss->buflen) - (ss->pendptr + ss->pendlen)) < len) {
+        /* "expand" buffer: actually we cannot really expand the buffer
+           here, because Apache's pool system doesn't support expanding chunks
+           of memory. Instead we have to either reuse processed data or
+           allocate a new chunk of memory in advance if we really need more
+           memory. */
+        int newlen;
+        char *newptr;
+
+        if ((  (ss->pendptr - ss->bufptr) 
+             + ((ss->bufptr + ss->buflen) - (ss->pendptr + ss->pendlen)) ) >= len) {
+            /* make memory available by reusing already processed data */
+            memmove(ss->bufptr, ss->pendptr, ss->pendlen);
+            ss->pendptr = ss->bufptr;
+        }
+        else {
+            /* too bad, we have to allocate a new larger buffer */
+            newlen = (ss->buflen * 2) + len;
+            newptr = ap_palloc(r->pool, newlen);
+            ss->bufptr  = newptr;
+            ss->buflen  = newlen;
+            memcpy(ss->bufptr, ss->pendptr, ss->pendlen);
+            ss->pendptr = ss->bufptr;
+        }
+    }
+    memcpy(ss->pendptr+ss->pendlen, buf, len);
+    ss->pendlen += len;
+    return;
+}
+
+/* finish request_rec after input sucking */
+static void ssl_io_suck_end(request_rec *r)
+{
+    struct ssl_io_suck_st *ss;
+    
+    if ((ss = ap_ctx_get(r->ctx, "ssl::io::suck")) == NULL)
+        return;
+    ss->active = TRUE;
+    r->read_body    = REQUEST_NO_BODY;
+    r->read_length  = 0;
+    r->read_chunked = 0;
+    r->remaining    = 0;
+    ap_bsetflag(r->connection->client, B_CHUNK, 0);
+    return;
+}
+
+void ssl_io_suck(request_rec *r, SSL *ssl)
+{
+    int rc;
+    int len;
+    char *buf;
+    int buflen;
+    char c;
+    int sucked;
+
+    if ((rc = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK)) == OK) {
+        if (ap_should_client_block(r)) {
+
+            /* read client request block through Apache API */
+            buflen = HUGE_STRING_LEN;
+            buf = ap_palloc(r->pool, buflen);
+            ap_hard_timeout("SSL I/O request body pre-sucking", r);
+            sucked = 0;
+            ssl_io_suck_start(r);
+            while ((len = ap_get_client_block(r, buf, buflen)) > 0) {
+                ssl_io_suck_record(r, buf, len);
+                sucked += len;
+            }
+            ssl_io_suck_end(r);
+            ap_kill_timeout(r);
+
+            /* suck trailing data (usually CR LF) which 
+               is still in the Apache BUFF layer */
+            while (ap_bpeekc(r->connection->client) != EOF) {
+                c = ap_bgetc(r->connection->client);
+                ssl_io_suck_record(r, &c, 1);
+                sucked++;
+            }
+
+            ssl_log(r->server, SSL_LOG_TRACE, 
+                    "I/O: sucked %d bytes of input data from SSL/TLS I/O layer "
+                    "for delayed injection into Apache I/O layer", sucked);
+        }
+    }
+    return;
+}
+    
+/* the SSL_read replacement routine which knows about the suck buffer */
+static int ssl_io_suck_read(SSL *ssl, char *buf, int len)
+{
+    ap_ctx *actx;
+    struct ssl_io_suck_st *ss;
+    request_rec *r = NULL;
+    int rv;
+
+    actx = (ap_ctx *)SSL_get_app_data2(ssl);
+    if (actx != NULL)
+        r = (request_rec *)ap_ctx_get(actx, "ssl::request_rec");
+
+    rv = -1;
+    if (r != NULL) {
+        ss = ap_ctx_get(r->ctx, "ssl::io::suck");
+        if (ss != NULL) {
+            if (ss->active && ss->pendlen > 0) {
+                /* ok, there is pre-sucked data */
+                len = (ss->pendlen > len ? len : ss->pendlen);
+                memcpy(buf, ss->pendptr, len);
+                ss->pendptr += len;
+                ss->pendlen -= len;
+                ssl_log(r->server, SSL_LOG_TRACE, 
+                        "I/O: injecting %d bytes of pre-sucked data "
+                        "into Apache I/O layer", len);
+                rv = len;
+            }
+        }
+    }
+    if (rv == -1)
+        rv = SSL_read(ssl, buf, len);
+    return rv;
+}
+
+/* override SSL_read in the following code... */
+#define SSL_read ssl_io_suck_read
+
+#endif /* !SSL_CONSERVATIVE */
+
+/*  _________________________________________________________________
+**
+**  I/O Hooks
+**  _________________________________________________________________
+*/
+
+#ifndef NO_WRITEV
+#include <sys/types.h>
+#include <sys/uio.h>
+#endif
+
+static int ssl_io_hook_read(BUFF *fb, char *buf, int len);
+static int ssl_io_hook_write(BUFF *fb, char *buf, int len);
+#ifndef NO_WRITEV
+static int ssl_io_hook_writev(BUFF *fb, const struct iovec *iov, int iovcnt);
+#endif
+#ifdef WIN32
+static int ssl_io_hook_recvwithtimeout(BUFF *fb, char *buf, int len);
+static int ssl_io_hook_sendwithtimeout(BUFF *fb, const char *buf, int len);
+#endif /* WIN32 */
+
+void ssl_io_register(void)
+{
+    ap_hook_register("ap::buff::read",   ssl_io_hook_read,  AP_HOOK_NOCTX);
+    ap_hook_register("ap::buff::write",  ssl_io_hook_write, AP_HOOK_NOCTX);
+#ifndef NO_WRITEV
+    ap_hook_register("ap::buff::writev", ssl_io_hook_writev, AP_HOOK_NOCTX);
+#endif
+#ifdef WIN32
+    ap_hook_register("ap::buff::recvwithtimeout",
+                     ssl_io_hook_recvwithtimeout, AP_HOOK_NOCTX);
+    ap_hook_register("ap::buff::sendwithtimeout",
+                     ssl_io_hook_sendwithtimeout, AP_HOOK_NOCTX);
+#endif
+    return;
+}
+
+void ssl_io_unregister(void)
+{
+    ap_hook_unregister("ap::buff::read",   ssl_io_hook_read);
+    ap_hook_unregister("ap::buff::write",  ssl_io_hook_write);
+#ifndef NO_WRITEV
+    ap_hook_unregister("ap::buff::writev", ssl_io_hook_writev);
+#endif
+#ifdef WIN32
+    ap_hook_unregister("ap::buff::recvwithtimeout", ssl_io_hook_recvwithtimeout);
+    ap_hook_unregister("ap::buff::sendwithtimeout", ssl_io_hook_sendwithtimeout);
+#endif
+    return;
+}
+
+static int ssl_io_hook_read(BUFF *fb, char *buf, int len)
+{
+    SSL *ssl;
+    conn_rec *c;
+    int rc;
+
+    if ((ssl = ap_ctx_get(fb->ctx, "ssl")) != NULL) {
+        rc = SSL_read(ssl, buf, len);
+        /*
+         * Simulate an EINTR in case OpenSSL wants to read more.
+         * (This is usually the case when the client forces an SSL
+         * renegotation which is handled implicitly by OpenSSL.)
+         */
+        if (rc < 0 && SSL_get_error(ssl, rc) == SSL_ERROR_WANT_READ)
+            errno = EINTR;
+        /*
+         * Log SSL errors
+         */
+        if (rc < 0 && SSL_get_error(ssl, rc) == SSL_ERROR_SSL) {
+            c = (conn_rec *)SSL_get_app_data(ssl);
+            ssl_log(c->server, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+                    "SSL error on reading data");
+        }
+        /*
+         * read(2) returns only the generic error number -1
+         */
+        if (rc < 0)
+            rc = -1;
+    }
+    else
+        rc = read(fb->fd_in, buf, len);
+    return rc;
+}
+
+static int ssl_io_hook_write(BUFF *fb, char *buf, int len)
+{
+    SSL *ssl;
+    conn_rec *c;
+    int rc;
+
+    if ((ssl = ap_ctx_get(fb->ctx, "ssl")) != NULL) {
+        rc = SSL_write(ssl, buf, len);
+        /*
+         * Simulate an EINTR in case OpenSSL wants to write more.
+         */
+        if (rc < 0 && SSL_get_error(ssl, rc) == SSL_ERROR_WANT_WRITE)
+            errno = EINTR;
+        /*
+         * Log SSL errors
+         */
+        if (rc < 0 && SSL_get_error(ssl, rc) == SSL_ERROR_SSL) {
+            c = (conn_rec *)SSL_get_app_data(ssl);
+            ssl_log(c->server, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+                    "SSL error on writing data");
+        }
+        /*
+         * write(2) returns only the generic error number -1
+         */
+        if (rc < 0)
+            rc = -1;
+    }
+    else
+        rc = write(fb->fd, buf, len);
+    return rc;
+}
+
+#ifndef NO_WRITEV
+/* the prototype for our own SSL_writev() */
+static int SSL_writev(SSL *, const struct iovec *, int);
+
+static int ssl_io_hook_writev(BUFF *fb, const struct iovec *iov, int iovcnt)
+{
+    SSL *ssl;
+    conn_rec *c;
+    int rc;
+
+    if ((ssl = ap_ctx_get(fb->ctx, "ssl")) != NULL) {
+        rc = SSL_writev(ssl, iov, iovcnt);
+        /*
+         * Simulate an EINTR in case OpenSSL wants to write more.
+         */
+        if (rc < 0 && SSL_get_error(ssl, rc) == SSL_ERROR_WANT_WRITE)
+            errno = EINTR;
+        /*
+         * Log SSL errors
+         */
+        if (rc < 0 && SSL_get_error(ssl, rc) == SSL_ERROR_SSL) {
+            c = (conn_rec *)SSL_get_app_data(ssl);
+            ssl_log(c->server, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+                    "SSL error on writing data");
+        }
+        /*
+         * writev(2) returns only the generic error number -1
+         */
+        if (rc < 0)
+            rc = -1;
+    }
+    else
+        rc = writev(fb->fd, iov, iovcnt);
+    return rc;
+}
+#endif
+
+#ifdef WIN32
+
+/* these two functions are exported from buff.c under WIN32 */
+API_EXPORT(int) sendwithtimeout(int sock, const char *buf, int len, int flags);
+API_EXPORT(int) recvwithtimeout(int sock, char *buf, int len, int flags);
+
+/* and the prototypes for our SSL_xxx variants */
+static int SSL_sendwithtimeout(BUFF *fb, const char *buf, int len);
+static int SSL_recvwithtimeout(BUFF *fb, char *buf, int len);
+
+static int ssl_io_hook_recvwithtimeout(BUFF *fb, char *buf, int len)
+{
+    SSL *ssl;
+    int rc;
+
+    if ((ssl = ap_ctx_get(fb->ctx, "ssl")) != NULL)
+        rc = SSL_recvwithtimeout(fb, buf, len);
+    else
+        rc = recvwithtimeout(fb->fd, buf, len, 0);
+    return rc;
+}
+
+static int ssl_io_hook_sendwithtimeout(BUFF *fb, const char *buf, int len)
+{
+    SSL *ssl;
+    int rc;
+
+    if ((ssl = ap_ctx_get(fb->ctx, "ssl")) != NULL)
+        rc = SSL_sendwithtimeout(fb, buf, len);
+    else
+        rc = sendwithtimeout(fb->fd, buf, len, 0);
+    return rc;
+}
+
+#endif /* WIN32 */
+
+/*  _________________________________________________________________
+**
+**  Special Functions for OpenSSL
+**  _________________________________________________________________
+*/
+
+#ifdef WIN32
+
+static int SSL_sendwithtimeout(BUFF *fb, const char *buf, int len)
+{
+    int iostate = 1;
+    fd_set fdset;
+    struct timeval tv;
+    int err = WSAEWOULDBLOCK;
+    int rv;
+    int retry;
+    int sock = fb->fd;
+    SSL *ssl;
+
+    ssl = ap_ctx_get(fb->ctx, "ssl");
+
+    if (!(tv.tv_sec = ap_check_alarm()))
+        return (SSL_write(ssl, (char*)buf, len));
+
+    rv = ioctlsocket(sock, FIONBIO, &iostate);
+    iostate = 0;
+    if (rv) {
+        err = WSAGetLastError();
+        ap_assert(0);
+    }
+    rv = SSL_write(ssl, (char*)buf, len);
+    if (rv <= 0) {
+        if (BIO_sock_should_retry(rv)) {
+            do {
+                retry = 0;
+                FD_ZERO(&fdset);
+                FD_SET((unsigned int)sock, &fdset);
+                tv.tv_usec = 0;
+                rv = select(FD_SETSIZE, NULL, &fdset, NULL, &tv);
+                if (rv == SOCKET_ERROR)
+                    err = WSAGetLastError();
+                else if (rv == 0) {
+                    ioctlsocket(sock, FIONBIO, &iostate);
+                    if(ap_check_alarm() < 0) {
+                        WSASetLastError(EINTR); /* Simulate an alarm() */
+                        return (SOCKET_ERROR);
+                    }
+                }
+                else {
+                    rv = SSL_write(ssl, (char*)buf, len);
+                    if (BIO_sock_should_retry(rv)) {
+                        ap_log_error(APLOG_MARK,APLOG_DEBUG, NULL,
+                                     "select claimed we could write, "
+                                     "but in fact we couldn't. "
+                                     "This is a bug in Windows.");
+                        retry = 1;
+                        Sleep(100);
+                    }
+                }
+            } while(retry);
+        }
+    }
+    ioctlsocket(sock, FIONBIO, &iostate);
+    if (rv == SOCKET_ERROR)
+        WSASetLastError(err);
+    return (rv);
+}
+
+static int SSL_recvwithtimeout(BUFF *fb, char *buf, int len)
+{
+    int iostate = 1;
+    fd_set fdset;
+    struct timeval tv;
+    int err = WSAEWOULDBLOCK;
+    int rv;
+    int sock = fb->fd_in;
+    SSL *ssl;
+    int retry;
+
+    ssl = ap_ctx_get(fb->ctx, "ssl");
+
+    if (!(tv.tv_sec = ap_check_alarm()))
+        return (SSL_read(ssl, buf, len));
+
+    rv = ioctlsocket(sock, FIONBIO, &iostate);
+    iostate = 0;
+    ap_assert(!rv);
+    rv = SSL_read(ssl, buf, len);
+    if (rv <= 0) {
+        if (BIO_sock_should_retry(rv)) {
+            do {
+                retry = 0;
+                FD_ZERO(&fdset);
+                FD_SET((unsigned int)sock, &fdset);
+                tv.tv_usec = 0;
+                rv = select(FD_SETSIZE, &fdset, NULL, NULL, &tv);
+                if (rv == SOCKET_ERROR)
+                    err = WSAGetLastError();
+                else if (rv == 0) {
+                    ioctlsocket(sock, FIONBIO, &iostate);
+                    ap_check_alarm();
+                    WSASetLastError(WSAEWOULDBLOCK);
+                    return (SOCKET_ERROR);
+                }
+                else {
+                    rv = SSL_read(ssl, buf, len);
+                    if (rv == SOCKET_ERROR) {
+                        if (BIO_sock_should_retry(rv)) {
+                          ap_log_error(APLOG_MARK,APLOG_DEBUG, NULL,
+                                       "select claimed we could read, "
+                                       "but in fact we couldn't. "
+                                       "This is a bug in Windows.");
+                          retry = 1;
+                          Sleep(100);
+                        }
+                        else {
+                            err = WSAGetLastError();
+                        }
+                    }
+                }
+            } while(retry);
+        }
+    }
+    ioctlsocket(sock, FIONBIO, &iostate);
+    if (rv == SOCKET_ERROR)
+        WSASetLastError(err);
+    return (rv);
+}
+
+#endif /*WIN32*/
+
+/*
+ * There is no SSL_writev() provided by OpenSSL. The reason is mainly because
+ * OpenSSL has to fragment the data itself again for the SSL record layer, so a
+ * writev() like interface makes not much sense.  What we do is to emulate it
+ * to at least being able to use the write() like interface. But keep in mind
+ * that the network I/O performance is not write() like, of course.
+ */
+#ifndef NO_WRITEV
+static int SSL_writev(SSL *ssl, const struct iovec *iov, int iovcnt)
+{
+    int i;
+    int n;
+    int rc;
+
+    rc = 0;
+    for (i = 0; i < iovcnt; i++) {
+        if ((n = SSL_write(ssl, iov[i].iov_base, iov[i].iov_len)) == -1) {
+            rc = -1;
+            break;
+        }
+        rc += n;
+    }
+    return rc;
+}
+#endif
+
+/*  _________________________________________________________________
+**
+**  I/O Data Debugging
+**  _________________________________________________________________
+*/
+
+#define DUMP_WIDTH 16
+
+static void ssl_io_data_dump(server_rec *srvr, const char *s, long len)
+{
+    char buf[256];
+    char tmp[64];
+    int i, j, rows, trunc;
+    unsigned char ch;
+
+    trunc = 0;
+    for(; (len > 0) && ((s[len-1] == ' ') || (s[len-1] == '\0')); len--)
+        trunc++;
+    rows = (len / DUMP_WIDTH);
+    if ((rows * DUMP_WIDTH) < len)
+        rows++;
+    ssl_log(srvr, SSL_LOG_DEBUG|SSL_NO_TIMESTAMP|SSL_NO_LEVELID,
+            "+-------------------------------------------------------------------------+");
+    for(i = 0 ; i< rows; i++) {
+        ap_snprintf(tmp, sizeof(tmp), "| %04x: ", i * DUMP_WIDTH);
+        ap_cpystrn(buf, tmp, sizeof(buf));
+        for (j = 0; j < DUMP_WIDTH; j++) {
+            if (((i * DUMP_WIDTH) + j) >= len)
+                ap_cpystrn(buf+strlen(buf), "   ", sizeof(buf)-strlen(buf));
+            else {
+                ch = ((unsigned char)*((char *)(s) + i * DUMP_WIDTH + j)) & 0xff;
+                ap_snprintf(tmp, sizeof(tmp), "%02x%c", ch , j==7 ? '-' : ' ');
+                ap_cpystrn(buf+strlen(buf), tmp, sizeof(buf)-strlen(buf));
+            }
+        }
+        ap_cpystrn(buf+strlen(buf), " ", sizeof(buf)-strlen(buf));
+        for (j = 0; j < DUMP_WIDTH; j++) {
+            if (((i * DUMP_WIDTH) + j) >= len)
+                ap_cpystrn(buf+strlen(buf), " ", sizeof(buf)-strlen(buf));
+            else {
+                ch = ((unsigned char)*((char *)(s) + i * DUMP_WIDTH + j)) & 0xff;
+                ap_snprintf(tmp, sizeof(tmp), "%c", ((ch >= ' ') && (ch <= '~')) ? ch : '.');
+                ap_cpystrn(buf+strlen(buf), tmp, sizeof(buf)-strlen(buf));
+            }
+        }
+        ap_cpystrn(buf+strlen(buf), " |", sizeof(buf)-strlen(buf));
+        ssl_log(srvr, SSL_LOG_DEBUG|SSL_NO_TIMESTAMP|SSL_NO_LEVELID, "%s", buf);
+    }
+    if (trunc > 0)
+        ssl_log(srvr, SSL_LOG_DEBUG|SSL_NO_TIMESTAMP|SSL_NO_LEVELID,
+                "| %04x - <SPACES/NULS>", len + trunc);
+    ssl_log(srvr, SSL_LOG_DEBUG|SSL_NO_TIMESTAMP|SSL_NO_LEVELID,
+            "+-------------------------------------------------------------------------+");
+    return;
+}
+
+long ssl_io_data_cb(BIO *bio, int cmd, const char *argp, int argi, long argl, long rc)
+{
+    SSL *ssl;
+    conn_rec *c;
+    server_rec *s;
+
+    if ((ssl = (SSL *)BIO_get_callback_arg(bio)) == NULL)
+        return rc;
+    if ((c = (conn_rec *)SSL_get_app_data(ssl)) == NULL)
+        return rc;
+    s = c->server;
+
+    if (   cmd == (BIO_CB_WRITE|BIO_CB_RETURN)
+        || cmd == (BIO_CB_READ |BIO_CB_RETURN) ) {
+        if (rc >= 0) {
+            ssl_log(s, SSL_LOG_DEBUG,
+                    "%s: %s %ld/%d bytes %s BIO#%08X [mem: %08lX] %s",
+                    SSL_LIBRARY_NAME,
+                    (cmd == (BIO_CB_WRITE|BIO_CB_RETURN) ? "write" : "read"),
+                    rc, argi, (cmd == (BIO_CB_WRITE|BIO_CB_RETURN) ? "to" : "from"),
+                    bio, argp,
+                    (argp != NULL ? "(BIO dump follows)" : "(Ops, no memory buffer?)"));
+            if (argp != NULL)
+                ssl_io_data_dump(s, argp, rc);
+        }
+        else {
+            ssl_log(s, SSL_LOG_DEBUG,
+                    "%s: I/O error, %d bytes expected to %s on BIO#%08X [mem: %08lX]",
+                    SSL_LIBRARY_NAME, argi,
+                    (cmd == (BIO_CB_WRITE|BIO_CB_RETURN) ? "write" : "read"),
+                    bio, argp);
+        }
+    }
+    return rc;
+}
+
diff --git a/modules/ssl/ssl_engine_kernel.c b/modules/ssl/ssl_engine_kernel.c
new file mode 100644 (file)
index 0000000..ca1b3f0
--- /dev/null
@@ -0,0 +1,1905 @@
+/*                      _             _
+**  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
+** | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
+** | | | | | | (_) | (_| |   \__ \__ \ |  www.modssl.org
+** |_| |_| |_|\___/ \__,_|___|___/___/_|  ftp.modssl.org
+**                      |_____|
+**  ssl_engine_kernel.c
+**  The SSL engine kernel
+*/
+
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ *    nor may "mod_ssl" appear in their names without prior
+ *    written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+/* ====================================================================
+ * Copyright (c) 1995-1999 Ben Laurie. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by Ben Laurie
+ *    for use in the Apache-SSL HTTP server project."
+ *
+ * 4. The name "Apache-SSL Server" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 5. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Ben Laurie
+ *    for use in the Apache-SSL HTTP server project."
+ *
+ * THIS SOFTWARE IS PROVIDED BY BEN LAURIE ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL BEN LAURIE OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+                             /* ``It took me fifteen years to discover
+                                  I had no talent for programming, but
+                                  I couldn't give it up because by that
+                                  time I was too famous.''
+                                            -- Unknown                */
+#include "mod_ssl.h"
+
+
+/*  _________________________________________________________________
+**
+**  SSL Engine Kernel
+**  _________________________________________________________________
+*/
+
+/*
+ *  Connect Handler:
+ *  Connect SSL to the accepted socket
+ *
+ *  Usually we would need an Apache API hook which is triggered right after
+ *  the socket is accepted for handling a new request. But Apache 1.3 doesn't
+ *  provide such a hook, so we have to patch http_main.c and call this
+ *  function directly.
+ */
+void ssl_hook_NewConnection(conn_rec *conn)
+{
+    server_rec *srvr;
+    BUFF *fb;
+    SSLSrvConfigRec *sc;
+    ap_ctx *apctx;
+    SSL *ssl;
+    char *cp;
+    char *cpVHostID;
+    char *cpVHostMD5;
+    X509 *xs;
+    int rc;
+
+    /*
+     * Get context
+     */
+    srvr = conn->server;
+    fb   = conn->client;
+    sc   = mySrvConfig(srvr);
+
+    /*
+     * Create SSL context
+     */
+    ap_ctx_set(fb->ctx, "ssl", NULL);
+
+    /*
+     * Immediately stop processing if SSL
+     * is disabled for this connection
+     */
+    if (sc == NULL || !sc->bEnabled)
+        return;
+
+    /*
+     * Remember the connection information for
+     * later access inside callback functions
+     */
+    cpVHostID = ssl_util_vhostid(conn->pool, srvr);
+    ssl_log(srvr, SSL_LOG_INFO, "Connection to child %d established "
+            "(server %s, client %s)", conn->child_num, cpVHostID, 
+            conn->remote_ip != NULL ? conn->remote_ip : "unknown");
+
+    /*
+     * Seed the Pseudo Random Number Generator (PRNG)
+     */
+    ssl_rand_seed(srvr, conn->pool, SSL_RSCTX_CONNECT, "");
+
+    /*
+     * Create a new SSL connection with the configured server SSL context and
+     * attach this to the socket. Additionally we register this attachment
+     * so we can detach later.
+     */
+    if ((ssl = SSL_new(sc->pSSLCtx)) == NULL) {
+        ssl_log(conn->server, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+                "Unable to create a new SSL connection from the SSL context");
+        ap_ctx_set(fb->ctx, "ssl", NULL);
+        ap_bsetflag(fb, B_EOF|B_EOUT, 1);
+        conn->aborted = 1;
+        return;
+    }
+    SSL_clear(ssl);
+    cpVHostMD5 = ap_md5(conn->pool, (unsigned char *)cpVHostID);
+    if (!SSL_set_session_id_context(ssl, (unsigned char *)cpVHostMD5, strlen(cpVHostMD5))) {
+        ssl_log(conn->server, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+                "Unable to set session id context to `%s'", cpVHostMD5);
+        ap_ctx_set(fb->ctx, "ssl", NULL);
+        ap_bsetflag(fb, B_EOF|B_EOUT, 1);
+        conn->aborted = 1;
+        return;
+    }
+    SSL_set_app_data(ssl, conn);
+    apctx = ap_ctx_new(conn->pool);
+    ap_ctx_set(apctx, "ssl::request_rec", NULL);
+    ap_ctx_set(apctx, "ssl::verify::depth", AP_CTX_NUM2PTR(0));
+    SSL_set_app_data2(ssl, apctx);
+    SSL_set_fd(ssl, fb->fd);
+    ap_ctx_set(fb->ctx, "ssl", ssl);
+
+    /*
+     *  Configure callbacks for SSL connection
+     */
+    SSL_set_tmp_rsa_callback(ssl, ssl_callback_TmpRSA);
+    SSL_set_tmp_dh_callback(ssl,  ssl_callback_TmpDH);
+    if (sc->nLogLevel >= SSL_LOG_DEBUG) {
+        BIO_set_callback(SSL_get_rbio(ssl), ssl_io_data_cb);
+        BIO_set_callback_arg(SSL_get_rbio(ssl), ssl);
+    }
+
+    /*
+     * Predefine some client verification results
+     */
+    ap_ctx_set(fb->ctx, "ssl::client::dn", NULL);
+    ap_ctx_set(fb->ctx, "ssl::verify::error", NULL);
+    ap_ctx_set(fb->ctx, "ssl::verify::info", NULL);
+    SSL_set_verify_result(ssl, X509_V_OK);
+
+    /*
+     * We have to manage a I/O timeout ourself, because Apache
+     * does it the first time when reading the request, but we're
+     * working some time before this happens.
+     */
+    ap_ctx_set(ap_global_ctx, "ssl::handshake::timeout", (void *)FALSE);
+    ap_set_callback_and_alarm(ssl_hook_TimeoutConnection, srvr->timeout);
+
+    /*
+     * Now enter the SSL Handshake Phase
+     */
+    while (!SSL_is_init_finished(ssl)) {
+
+        if ((rc = SSL_accept(ssl)) <= 0) {
+
+            if (SSL_get_error(ssl, rc) == SSL_ERROR_ZERO_RETURN) {
+                /*
+                 * The case where the connection was closed before any data
+                 * was transferred. That's not a real error and can occur
+                 * sporadically with some clients.
+                 */
+                ssl_log(srvr, SSL_LOG_INFO,
+                        "SSL handshake stopped: connection was closed");
+                SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN);
+                SSL_smart_shutdown(ssl);
+                SSL_free(ssl);
+                ap_ctx_set(fb->ctx, "ssl", NULL);
+                ap_bsetflag(fb, B_EOF|B_EOUT, 1);
+                conn->aborted = 1;
+                return;
+            }
+            else if (ERR_GET_REASON(ERR_peek_error()) == SSL_R_HTTP_REQUEST) {
+                /*
+                 * The case where OpenSSL has recognized a HTTP request:
+                 * This means the client speaks plain HTTP on our HTTPS
+                 * port. Hmmmm...  At least for this error we can be more friendly
+                 * and try to provide him with a HTML error page. We have only one
+                 * problem: OpenSSL has already read some bytes from the HTTP
+                 * request. So we have to skip the request line manually and
+                 * instead provide a faked one in order to continue the internal
+                 * Apache processing.
+                 *
+                 */
+                char ca[2];
+                int rv;
+
+                /* log the situation */
+                ssl_log(srvr, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+                        "SSL handshake failed: HTTP spoken on HTTPS port; "
+                        "trying to send HTML error page");
+
+                /* first: skip the remaining bytes of the request line */
+                do {
+                    do {
+                        rv = read(fb->fd, ca, 1);
+                    } while (rv == -1 && errno == EINTR);
+                } while (rv > 0 && ca[0] != '\012' /*LF*/);
+
+                /* second: fake the request line */
+                fb->inbase = ap_palloc(fb->pool, fb->bufsiz);
+                ap_cpystrn((char *)fb->inbase, "GET /mod_ssl:error:HTTP-request HTTP/1.0\r\n",
+                           fb->bufsiz);
+                fb->inptr = fb->inbase;
+                fb->incnt = strlen((char *)fb->inptr);
+
+                /* third: kick away the SSL stuff */
+                SSL_set_shutdown(ssl, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN);
+                SSL_smart_shutdown(ssl);
+                SSL_free(ssl);
+                ap_ctx_set(fb->ctx, "ssl", NULL);
+
+                /* finally: let Apache go on with processing */
+                return;
+            }
+            else if (ap_ctx_get(ap_global_ctx, "ssl::handshake::timeout") == (void *)TRUE) {
+                ssl_log(srvr, SSL_LOG_ERROR,
+                        "SSL handshake timed out (client %s, server %s)",
+                        conn->remote_ip != NULL ? conn->remote_ip : "unknown", cpVHostID);
+                SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN);
+                SSL_smart_shutdown(ssl);
+                SSL_free(ssl);
+                ap_ctx_set(fb->ctx, "ssl", NULL);
+                ap_bsetflag(fb, B_EOF|B_EOUT, 1);
+                conn->aborted = 1;
+                return;
+            }
+            else if (SSL_get_error(ssl, rc) == SSL_ERROR_SYSCALL) {
+                if (errno == EINTR)
+                    continue;
+                if (errno > 0)
+                    ssl_log(srvr, SSL_LOG_ERROR|SSL_ADD_SSLERR|SSL_ADD_ERRNO,
+                            "SSL handshake interrupted by system "
+                            "[Hint: Stop button pressed in browser?!]");
+                else
+                    ssl_log(srvr, SSL_LOG_INFO|SSL_ADD_SSLERR|SSL_ADD_ERRNO,
+                            "Spurious SSL handshake interrupt"
+                            "[Hint: Usually just one of those OpenSSL confusions!?]");
+                SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN);
+                SSL_smart_shutdown(ssl);
+                SSL_free(ssl);
+                ap_ctx_set(fb->ctx, "ssl", NULL);
+                ap_bsetflag(fb, B_EOF|B_EOUT, 1);
+                conn->aborted = 1;
+                return;
+            }
+            else {
+                /*
+                 * Ok, anything else is a fatal error
+                 */
+                ssl_log(srvr, SSL_LOG_ERROR|SSL_ADD_SSLERR|SSL_ADD_ERRNO,
+                        "SSL handshake failed (server %s, client %s)", cpVHostID, 
+                        conn->remote_ip != NULL ? conn->remote_ip : "unknown");
+
+                /*
+                 * try to gracefully shutdown the connection:
+                 * - send an own shutdown message (be gracefully)
+                 * - don't wait for peer's shutdown message (deadloop)
+                 * - kick away the SSL stuff immediately
+                 * - block the socket, so Apache cannot operate any more
+                 */
+                SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN);
+                SSL_smart_shutdown(ssl);
+                SSL_free(ssl);
+                ap_ctx_set(fb->ctx, "ssl", NULL);
+                ap_bsetflag(fb, B_EOF|B_EOUT, 1);
+                conn->aborted = 1;
+                return;
+            }
+        }
+
+        /*
+         * Check for failed client authentication
+         */
+        if (   SSL_get_verify_result(ssl) != X509_V_OK
+            || ap_ctx_get(fb->ctx, "ssl::verify::error") != NULL) {
+            cp = (char *)ap_ctx_get(fb->ctx, "ssl::verify::error");
+            ssl_log(srvr, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+                    "SSL client authentication failed: %s", 
+                    cp != NULL ? cp : "unknown reason");
+            SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN);
+            SSL_smart_shutdown(ssl);
+            SSL_free(ssl);
+            ap_ctx_set(fb->ctx, "ssl", NULL);
+            ap_bsetflag(fb, B_EOF|B_EOUT, 1);
+            conn->aborted = 1;
+            return;
+        }
+
+        /*
+         * Remember the peer certificate's DN
+         */
+        if ((xs = SSL_get_peer_certificate(ssl)) != NULL) {
+            cp = X509_NAME_oneline(X509_get_subject_name(xs), NULL, 0);
+            ap_ctx_set(fb->ctx, "ssl::client::dn", ap_pstrdup(conn->pool, cp));
+            free(cp);
+        }
+
+        /*
+         * Make really sure that when a peer certificate
+         * is required we really got one... (be paranoid)
+         */
+        if (   sc->nVerifyClient == SSL_CVERIFY_REQUIRE
+            && ap_ctx_get(fb->ctx, "ssl::client::dn") == NULL) {
+            ssl_log(srvr, SSL_LOG_ERROR,
+                    "No acceptable peer certificate available");
+            SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN);
+            SSL_smart_shutdown(ssl);
+            SSL_free(ssl);
+            ap_ctx_set(fb->ctx, "ssl", NULL);
+            ap_bsetflag(fb, B_EOF|B_EOUT, 1);
+            conn->aborted = 1;
+            return;
+        }
+    }
+
+    /*
+     * Remove the timeout handling
+     */
+    ap_set_callback_and_alarm(NULL, 0);
+    ap_ctx_set(ap_global_ctx, "ssl::handshake::timeout", (void *)FALSE);
+
+    /*
+     * Improve I/O throughput by using
+     * OpenSSL's read-ahead functionality
+     * (don't used under Win32, because
+     * there we use select())
+     */
+#ifndef WIN32
+    SSL_set_read_ahead(ssl, TRUE);
+#endif
+
+#ifdef SSL_VENDOR
+    /* Allow vendors to do more things on connection time... */
+    ap_hook_use("ap::mod_ssl::vendor::new_connection",
+                AP_HOOK_SIG2(void,ptr), AP_HOOK_ALL, conn);
+#endif
+
+    return;
+}
+
+/*
+ * Signal handler function for the SSL handshake phase
+ */
+void ssl_hook_TimeoutConnection(int sig)
+{
+    /* we just set a flag for the handshake processing loop */
+    ap_ctx_set(ap_global_ctx, "ssl::handshake::timeout", (void *)TRUE);
+    return;
+}
+
+/*
+ *  Close the SSL part of the socket connection
+ *  (called immediately _before_ the socket is closed)
+ */
+void ssl_hook_CloseConnection(conn_rec *conn)
+{
+    SSL *ssl;
+    char *cpType;
+
+    ssl = ap_ctx_get(conn->client->ctx, "ssl");
+    if (ssl == NULL)
+        return;
+
+    /*
+     * First make sure that no more data is pending in Apache's BUFF,
+     * because when it's (implicitly) flushed later by the ap_bclose()
+     * calls of Apache it would lead to an I/O error in the browser due
+     * to the fact that the SSL layer was already removed by us.
+     */
+    ap_bflush(conn->client);
+
+    /*
+     * Now close the SSL layer of the connection. We've to take
+     * the TLSv1 standard into account here:
+     *
+     * | 7.2.1. Closure alerts
+     * |
+     * | The client and the server must share knowledge that the connection is
+     * | ending in order to avoid a truncation attack. Either party may
+     * | initiate the exchange of closing messages.
+     * |
+     * | close_notify
+     * |     This message notifies the recipient that the sender will not send
+     * |     any more messages on this connection. The session becomes
+     * |     unresumable if any connection is terminated without proper
+     * |     close_notify messages with level equal to warning.
+     * |
+     * | Either party may initiate a close by sending a close_notify alert.
+     * | Any data received after a closure alert is ignored.
+     * |
+     * | Each party is required to send a close_notify alert before closing
+     * | the write side of the connection. It is required that the other party
+     * | respond with a close_notify alert of its own and close down the
+     * | connection immediately, discarding any pending writes. It is not
+     * | required for the initiator of the close to wait for the responding
+     * | close_notify alert before closing the read side of the connection.
+     *
+     * This means we've to send a close notify message, but haven't to wait
+     * for the close notify of the client. Actually we cannot wait for the
+     * close notify of the client because some clients (including Netscape
+     * 4.x) don't send one, so we would hang.
+     */
+
+    /*
+     * exchange close notify messages, but allow the user
+     * to force the type of handshake via SetEnvIf directive
+     */
+    if (ap_ctx_get(conn->client->ctx, "ssl::flag::unclean-shutdown") == PTRUE) {
+        /* perform no close notify handshake at all
+           (violates the SSL/TLS standard!) */
+        SSL_set_shutdown(ssl, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN);
+        cpType = "unclean";
+    }
+    else if (ap_ctx_get(conn->client->ctx, "ssl::flag::accurate-shutdown") == PTRUE) {
+        /* send close notify and wait for clients close notify
+           (standard compliant, but usually causes connection hangs) */
+        SSL_set_shutdown(ssl, 0);
+        cpType = "accurate";
+    }
+    else {
+        /* send close notify, but don't wait for clients close notify
+           (standard compliant and safe, so it's the DEFAULT!) */
+        SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN);
+        cpType = "standard";
+    }
+    SSL_smart_shutdown(ssl);
+
+    /* deallocate the SSL connection */
+    SSL_free(ssl);
+    ap_ctx_set(conn->client->ctx, "ssl", NULL);
+
+    /* and finally log the fact that we've closed the connection */
+    ssl_log(conn->server, SSL_LOG_INFO,
+            "Connection to child %d closed with %s shutdown (server %s, client %s)",
+            conn->child_num, cpType, ssl_util_vhostid(conn->pool, conn->server),
+            conn->remote_ip != NULL ? conn->remote_ip : "unknown");
+    return;
+}
+
+/*
+ *  Post Read Request Handler
+ */
+int ssl_hook_ReadReq(request_rec *r)
+{
+    SSL *ssl;
+    ap_ctx *apctx;
+
+    /*
+     * Get the SSL connection structure and perform the
+     * delayed interlinking from SSL back to request_rec
+     */
+    ssl = ap_ctx_get(r->connection->client->ctx, "ssl");
+    if (ssl != NULL) {
+        apctx = SSL_get_app_data2(ssl);
+        ap_ctx_set(apctx, "ssl::request_rec", r);
+    }
+
+    /*
+     * Force the mod_ssl content handler when URL indicates this
+     */
+    if (strEQn(r->uri, "/mod_ssl:", 9))
+        r->handler = "mod_ssl:content-handler";
+    if (ssl != NULL) {
+        ap_ctx_set(r->ctx, "ap::http::method",  "https");
+        ap_ctx_set(r->ctx, "ap::default::port", "443");
+    }
+    else {
+        ap_ctx_set(r->ctx, "ap::http::method",  NULL);
+        ap_ctx_set(r->ctx, "ap::default::port", NULL);
+    }
+    return DECLINED;
+}
+
+/*
+ *  URL Translation Handler
+ */
+int ssl_hook_Translate(request_rec *r)
+{
+    if (ap_ctx_get(r->connection->client->ctx, "ssl") == NULL)
+        return DECLINED;
+
+    /*
+     * Log information about incoming HTTPS requests
+     */
+    if (ap_is_initial_req(r))
+        ssl_log(r->server, SSL_LOG_INFO,
+                "%s HTTPS request received for child %d (server %s)",
+                r->connection->keepalives <= 0 ?
+                    "Initial (No.1)" :
+                    ap_psprintf(r->pool, "Subsequent (No.%d)",
+                                r->connection->keepalives+1),
+                r->connection->child_num,
+                ssl_util_vhostid(r->pool, r->server));
+
+    /*
+     * Move SetEnvIf information from request_rec to conn_rec/BUFF
+     * to allow the close connection handler to use them.
+     */
+    if (ap_table_get(r->subprocess_env, "ssl-unclean-shutdown") != NULL)
+        ap_ctx_set(r->connection->client->ctx, "ssl::flag::unclean-shutdown", PTRUE);
+    else
+        ap_ctx_set(r->connection->client->ctx, "ssl::flag::unclean-shutdown", PFALSE);
+    if (ap_table_get(r->subprocess_env, "ssl-accurate-shutdown") != NULL)
+        ap_ctx_set(r->connection->client->ctx, "ssl::flag::accurate-shutdown", PTRUE);
+    else
+        ap_ctx_set(r->connection->client->ctx, "ssl::flag::accurate-shutdown", PFALSE);
+
+    return DECLINED;
+}
+
+/*
+ *  Content Handler
+ */
+int ssl_hook_Handler(request_rec *r)
+{
+    int port;
+    char *thisport;
+    char *thisurl;
+
+    if (strNEn(r->uri, "/mod_ssl:", 9))
+        return DECLINED;
+
+    if (strEQ(r->uri, "/mod_ssl:error:HTTP-request")) {
+        thisport = "";
+        port = ap_get_server_port(r);
+        if (!ap_is_default_port(port, r))
+            thisport = ap_psprintf(r->pool, ":%u", port);
+        thisurl = ap_psprintf(r->pool, "https://%s%s/",
+                              ap_get_server_name(r), thisport);
+
+        ap_table_setn(r->notes, "error-notes", ap_psprintf(r->pool,
+                      "Reason: You're speaking plain HTTP to an SSL-enabled server port.<BR>\n"
+                      "Instead use the HTTPS scheme to access this URL, please.<BR>\n"
+                      "<BLOCKQUOTE>Hint: <A HREF=\"%s\"><B>%s</B></A></BLOCKQUOTE>",
+                      thisurl, thisurl));
+    }
+
+    return HTTP_BAD_REQUEST;
+}
+
+/*
+ *  Access Handler
+ */
+int ssl_hook_Access(request_rec *r)
+{
+    SSLDirConfigRec *dc;
+    SSLSrvConfigRec *sc;
+    SSL *ssl;
+    SSL_CTX *ctx = NULL;
+    array_header *apRequirement;
+    ssl_require_t *pRequirements;
+    ssl_require_t *pRequirement;
+    char *cp;
+    int ok;
+    int i;
+    BOOL renegotiate;
+    BOOL renegotiate_quick;
+#ifdef SSL_EXPERIMENTAL_PERDIRCA
+    BOOL reconfigured_locations;
+    STACK_OF(X509_NAME) *skCAList;
+    char *cpCAPath;
+    char *cpCAFile;
+#endif
+    X509 *cert;
+    STACK_OF(X509) *certstack;
+    X509_STORE *certstore;
+    X509_STORE_CTX certstorectx;
+    int depth;
+    STACK_OF(SSL_CIPHER) *skCipherOld;
+    STACK_OF(SSL_CIPHER) *skCipher;
+    SSL_CIPHER *pCipher;
+    ap_ctx *apctx;
+    int nVerifyOld;
+    int nVerify;
+    int n;
+    void *vp;
+    int rc;
+
+    dc  = myDirConfig(r);
+    sc  = mySrvConfig(r->server);
+    ssl = ap_ctx_get(r->connection->client->ctx, "ssl");
+    if (ssl != NULL)
+        ctx = SSL_get_SSL_CTX(ssl);
+
+    /*
+     * Support for SSLRequireSSL directive
+     */
+    if (dc->bSSLRequired && ssl == NULL) {
+        ap_log_reason("SSL connection required", r->filename, r);
+        /* remember forbidden access for strict require option */
+        ap_table_setn(r->notes, "ssl-access-forbidden", (void *)1);
+        return FORBIDDEN;
+    }
+
+    /*
+     * Check to see if SSL protocol is on
+     */
+    if (!sc->bEnabled)
+        return DECLINED;
+    if (ssl == NULL)
+        return DECLINED;
+
+    /*
+     * Support for per-directory reconfigured SSL connection parameters.
+     *
+     * This is implemented by forcing an SSL renegotiation with the
+     * reconfigured parameter suite. But Apache's internal API processing
+     * makes our life very hard here, because when internal sub-requests occur
+     * we nevertheless should avoid multiple unnecessary SSL handshakes (they
+     * require extra network I/O and especially time to perform). 
+     * 
+     * But the optimization for filtering out the unnecessary handshakes isn't
+     * obvious and trivial.  Especially because while Apache is in its
+     * sub-request processing the client could force additional handshakes,
+     * too. And these take place perhaps without our notice. So the only
+     * possibility is to explicitly _ask_ OpenSSL whether the renegotiation
+     * has to be performed or not. It has to performed when some parameters
+     * which were previously known (by us) are not those we've now
+     * reconfigured (as known by OpenSSL) or (in optimized way) at least when
+     * the reconfigured parameter suite is stronger (more restrictions) than
+     * the currently active one.
+     */
+    renegotiate            = FALSE;
+    renegotiate_quick      = FALSE;
+#ifdef SSL_EXPERIMENTAL_PERDIRCA
+    reconfigured_locations = FALSE;
+#endif
+
+    /*
+     * Override of SSLCipherSuite
+     *
+     * We provide two options here:
+     *
+     * o The paranoid and default approach where we force a renegotiation when
+     *   the cipher suite changed in _any_ way (which is straight-forward but
+     *   often forces renegotiations too often and is perhaps not what the
+     *   user actually wanted).
+     *
+     * o The optimized and still secure way where we force a renegotiation
+     *   only if the currently active cipher is no longer contained in the
+     *   reconfigured/new cipher suite. Any other changes are not important
+     *   because it's the servers choice to select a cipher from the ones the
+     *   client supports. So as long as the current cipher is still in the new
+     *   cipher suite we're happy. Because we can assume we would have
+     *   selected it again even when other (better) ciphers exists now in the
+     *   new cipher suite. This approach is fine because the user explicitly
+     *   has to enable this via ``SSLOptions +OptRenegotiate''. So we do no
+     *   implicit optimizations.
+     */
+    if (dc->szCipherSuite != NULL) {
+        /* remember old state */
+        pCipher = NULL;
+        skCipherOld = NULL;
+        if (dc->nOptions & SSL_OPT_OPTRENEGOTIATE)
+            pCipher = SSL_get_current_cipher(ssl);
+        else {
+            skCipherOld = SSL_get_ciphers(ssl);
+            if (skCipherOld != NULL)
+                skCipherOld = sk_SSL_CIPHER_dup(skCipherOld);
+        }
+        /* configure new state */
+        if (!SSL_set_cipher_list(ssl, dc->szCipherSuite)) {
+            ssl_log(r->server, SSL_LOG_WARN|SSL_ADD_SSLERR,
+                    "Unable to reconfigure (per-directory) permitted SSL ciphers");
+            if (skCipherOld != NULL)
+                sk_SSL_CIPHER_free(skCipherOld);
+            return FORBIDDEN;
+        }
+        /* determine whether a renegotiation has to be forced */
+        skCipher = SSL_get_ciphers(ssl);
+        if (dc->nOptions & SSL_OPT_OPTRENEGOTIATE) {
+            /* optimized way */
+            if ((pCipher == NULL && skCipher != NULL) ||
+                (pCipher != NULL && skCipher == NULL)   )
+                renegotiate = TRUE;
+            else if (pCipher != NULL && skCipher != NULL
+                     && sk_SSL_CIPHER_find(skCipher, pCipher) < 0) {
+                renegotiate = TRUE;
+            }
+        }
+        else {
+            /* paranoid way */
+            if ((skCipherOld == NULL && skCipher != NULL) ||
+                (skCipherOld != NULL && skCipher == NULL)   )
+                renegotiate = TRUE;
+            else if (skCipherOld != NULL && skCipher != NULL) {
+                for (n = 0; !renegotiate && n < sk_SSL_CIPHER_num(skCipher); n++) {
+                    if (sk_SSL_CIPHER_find(skCipherOld, sk_SSL_CIPHER_value(skCipher, n)) < 0)
+                        renegotiate = TRUE;
+                }
+                for (n = 0; !renegotiate && n < sk_SSL_CIPHER_num(skCipherOld); n++) {
+                    if (sk_SSL_CIPHER_find(skCipher, sk_SSL_CIPHER_value(skCipherOld, n)) < 0)
+                        renegotiate = TRUE;
+                }
+            }
+        }
+        /* cleanup */
+        if (skCipherOld != NULL)
+            sk_SSL_CIPHER_free(skCipherOld);
+        /* tracing */
+        if (renegotiate)
+            ssl_log(r->server, SSL_LOG_TRACE,
+                    "Reconfigured cipher suite will force renegotiation");
+    }
+
+    /*
+     * override of SSLVerifyDepth
+     *
+     * The depth checks are handled by us manually inside the verify callback
+     * function and not by OpenSSL internally (and our function is aware of
+     * both the per-server and per-directory contexts). So we cannot ask
+     * OpenSSL about the currently verify depth. Instead we remember it in our
+     * ap_ctx attached to the SSL* of OpenSSL.  We've to force the
+     * renegotiation if the reconfigured/new verify depth is less than the
+     * currently active/remembered verify depth (because this means more
+     * restriction on the certificate chain).
+     */
+    if (dc->nVerifyDepth != UNSET) {
+        apctx = SSL_get_app_data2(ssl);
+        if ((vp = ap_ctx_get(apctx, "ssl::verify::depth")) != NULL)
+            n = (int)AP_CTX_PTR2NUM(vp);
+        else
+            n = sc->nVerifyDepth;
+        ap_ctx_set(apctx, "ssl::verify::depth",
+                   AP_CTX_NUM2PTR(dc->nVerifyDepth));
+        /* determine whether a renegotiation has to be forced */
+        if (dc->nVerifyDepth < n) {
+            renegotiate = TRUE;
+            ssl_log(r->server, SSL_LOG_TRACE,
+                    "Reduced client verification depth will force renegotiation");
+        }
+    }
+
+    /*
+     * override of SSLVerifyClient
+     *
+     * We force a renegotiation if the reconfigured/new verify type is
+     * stronger than the currently active verify type. 
+     *
+     * The order is: none << optional_no_ca << optional << require
+     *
+     * Additionally the following optimization is possible here: When the
+     * currently active verify type is "none" but a client certificate is
+     * already known/present, it's enough to manually force a client
+     * verification but at least skip the I/O-intensive renegotation
+     * handshake.
+     */
+    if (dc->nVerifyClient != SSL_CVERIFY_UNSET) {
+        /* remember old state */
+        nVerifyOld = SSL_get_verify_mode(ssl);
+        /* configure new state */
+        nVerify = SSL_VERIFY_NONE;
+        if (dc->nVerifyClient == SSL_CVERIFY_REQUIRE)
+            nVerify |= SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
+        if (   (dc->nVerifyClient == SSL_CVERIFY_OPTIONAL)
+            || (dc->nVerifyClient == SSL_CVERIFY_OPTIONAL_NO_CA) )
+            nVerify |= SSL_VERIFY_PEER;
+        SSL_set_verify(ssl, nVerify, ssl_callback_SSLVerify);
+        SSL_set_verify_result(ssl, X509_V_OK);
+        /* determine whether we've to force a renegotiation */
+        if (nVerify != nVerifyOld) {
+            if (   (   (nVerifyOld == SSL_VERIFY_NONE)
+                    && (nVerify    != SSL_VERIFY_NONE))
+                || (  !(nVerifyOld &  SSL_VERIFY_PEER)
+                    && (nVerify    &  SSL_VERIFY_PEER))
+                || (  !(nVerifyOld &  (SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT))
+                    && (nVerify    &  (SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT)))) {
+                renegotiate = TRUE;
+                /* optimization */
+                if (   dc->nOptions & SSL_OPT_OPTRENEGOTIATE
+                    && nVerifyOld == SSL_VERIFY_NONE
+                    && SSL_get_peer_certificate(ssl) != NULL)
+                    renegotiate_quick = TRUE;
+                ssl_log(r->server, SSL_LOG_TRACE,
+                        "Changed client verification type will force %srenegotiation",
+                        renegotiate_quick ? "quick " : "");
+             }
+        }
+    }
+
+    /*
+     *  override SSLCACertificateFile & SSLCACertificatePath
+     *  This is tagged experimental because it has to use an ugly kludge: We
+     *  have to change the locations inside the SSL_CTX* (per-server global)
+     *  instead inside SSL* (per-connection local) and reconfigure it to the
+     *  old values later. That's problematic at least for the threaded process
+     *  model of Apache under Win32 or when an error occurs. But unless
+     *  OpenSSL provides a SSL_load_verify_locations() function we've no other
+     *  chance to provide this functionality...
+     */
+#ifdef SSL_EXPERIMENTAL_PERDIRCA
+    if (   (   dc->szCACertificateFile != NULL
+            && (   sc->szCACertificateFile == NULL
+                || (   sc->szCACertificateFile != NULL
+                    && strNE(dc->szCACertificateFile, sc->szCACertificateFile))))
+        || (   dc->szCACertificatePath != NULL
+            && (   sc->szCACertificatePath == NULL
+                || (   sc->szCACertificatePath != NULL
+                    && strNE(dc->szCACertificatePath, sc->szCACertificatePath)))) ) {
+        cpCAFile = dc->szCACertificateFile != NULL ?
+                   dc->szCACertificateFile : sc->szCACertificateFile;
+        cpCAPath = dc->szCACertificatePath != NULL ?
+                   dc->szCACertificatePath : sc->szCACertificatePath;
+        /*
+           FIXME: This should be...
+           if (!SSL_load_verify_locations(ssl, cpCAFile, cpCAPath)) {
+           ...but OpenSSL still doesn't provide this!
+         */
+        if (!SSL_CTX_load_verify_locations(ctx, cpCAFile, cpCAPath)) {
+            ssl_log(r->server, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+                    "Unable to reconfigure verify locations "
+                    "for client authentication");
+            return FORBIDDEN;
+        }
+        if ((skCAList = ssl_init_FindCAList(r->server, r->pool,
+                                            cpCAFile, cpCAPath)) == NULL) {
+            ssl_log(r->server, SSL_LOG_ERROR,
+                    "Unable to determine list of available "
+                    "CA certificates for client authentication");
+            return FORBIDDEN;
+        }
+        SSL_set_client_CA_list(ssl, skCAList);
+        renegotiate = TRUE;
+        reconfigured_locations = TRUE;
+        ssl_log(r->server, SSL_LOG_TRACE,
+                "Changed client verification locations will force renegotiation");
+    }
+#endif /* SSL_EXPERIMENTAL_PERDIRCA */
+
+#ifdef SSL_CONSERVATIVE 
+    /* 
+     *  SSL renegotiations in conjunction with HTTP
+     *  requests using the POST method are not supported.
+     */
+    if (renegotiate && r->method_number == M_POST) {
+        ssl_log(r->server, SSL_LOG_ERROR,
+                "SSL Re-negotiation in conjunction with POST method not supported!");
+        ssl_log(r->server, SSL_LOG_INFO,
+                "You have to compile without -DSSL_CONSERVATIVE to enabled support for this.");
+        return METHOD_NOT_ALLOWED;
+    }
+#endif /* SSL_CONSERVATIVE */
+
+    /*
+     * now do the renegotiation if anything was actually reconfigured
+     */
+    if (renegotiate) {
+        /*
+         * Now we force the SSL renegotation by sending the Hello Request
+         * message to the client. Here we have to do a workaround: Actually
+         * OpenSSL returns immediately after sending the Hello Request (the
+         * intent AFAIK is because the SSL/TLS protocol says it's not a must
+         * that the client replies to a Hello Request). But because we insist
+         * on a reply (anything else is an error for us) we have to go to the
+         * ACCEPT state manually. Using SSL_set_accept_state() doesn't work
+         * here because it resets too much of the connection.  So we set the
+         * state explicitly and continue the handshake manually.
+         */
+        ssl_log(r->server, SSL_LOG_INFO, "Requesting connection re-negotiation");
+        if (renegotiate_quick) {
+            /* perform just a manual re-verification of the peer */
+            ssl_log(r->server, SSL_LOG_TRACE,
+                    "Performing quick renegotiation: just re-verifying the peer");
+            certstore = SSL_CTX_get_cert_store(ctx);
+            if (certstore == NULL) {
+                ssl_log(r->server, SSL_LOG_ERROR, "Cannot find certificate storage");
+                return FORBIDDEN;
+            }
+            certstack = SSL_get_peer_cert_chain(ssl);
+            if (certstack == NULL || sk_X509_num(certstack) == 0) {
+                ssl_log(r->server, SSL_LOG_ERROR, "Cannot find peer certificate chain");
+                return FORBIDDEN;
+            }
+            cert = sk_X509_value(certstack, 0);
+            X509_STORE_CTX_init(&certstorectx, certstore, cert, certstack);
+            depth = SSL_get_verify_depth(ssl);
+            if (depth >= 0)
+                X509_STORE_CTX_set_depth(&certstorectx, depth);
+            X509_STORE_CTX_set_ex_data(&certstorectx,
+                SSL_get_ex_data_X509_STORE_CTX_idx(), (char *)ssl);
+            if (!X509_verify_cert(&certstorectx))
+                ssl_log(r->server, SSL_LOG_ERROR|SSL_ADD_SSLERR, 
+                        "Re-negotiation verification step failed");
+            SSL_set_verify_result(ssl, certstorectx.error);
+            X509_STORE_CTX_cleanup(&certstorectx);
+        }
+        else {
+            /* do a full renegotiation */
+            ssl_log(r->server, SSL_LOG_TRACE,
+                    "Performing full renegotiation: complete handshake protocol");
+            if (r->main != NULL)
+                SSL_set_session_id_context(ssl, (unsigned char *)&(r->main), sizeof(r->main));
+            else
+                SSL_set_session_id_context(ssl, (unsigned char *)&r, sizeof(r));
+#ifndef SSL_CONSERVATIVE
+            ssl_io_suck(r, ssl);
+#endif
+            SSL_renegotiate(ssl);
+            SSL_do_handshake(ssl);
+            if (SSL_get_state(ssl) != SSL_ST_OK) {
+                ssl_log(r->server, SSL_LOG_ERROR, "Re-negotiation request failed");
+                return FORBIDDEN;
+            }
+            ssl_log(r->server, SSL_LOG_INFO, "Awaiting re-negotiation handshake");
+            SSL_set_state(ssl, SSL_ST_ACCEPT);
+            SSL_do_handshake(ssl);
+            if (SSL_get_state(ssl) != SSL_ST_OK) {
+                ssl_log(r->server, SSL_LOG_ERROR,
+                        "Re-negotiation handshake failed: Not accepted by client!?");
+                return FORBIDDEN;
+            }
+        }
+
+        /*
+         * Remember the peer certificate's DN
+         */
+        if ((cert = SSL_get_peer_certificate(ssl)) != NULL) {
+            cp = X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0);
+            ap_ctx_set(r->connection->client->ctx, "ssl::client::dn", 
+                       ap_pstrdup(r->connection->pool, cp));
+            free(cp);
+        }
+
+        /*
+         * Finally check for acceptable renegotiation results
+         */
+        if (dc->nVerifyClient != SSL_CVERIFY_NONE) {
+            if (   dc->nVerifyClient == SSL_CVERIFY_REQUIRE
+                && SSL_get_verify_result(ssl) != X509_V_OK  ) {
+                ssl_log(r->server, SSL_LOG_ERROR,
+                        "Re-negotiation handshake failed: Client verification failed");
+                return FORBIDDEN;
+            }
+            if (   dc->nVerifyClient == SSL_CVERIFY_REQUIRE
+                && SSL_get_peer_certificate(ssl) == NULL   ) {
+                ssl_log(r->server, SSL_LOG_ERROR,
+                        "Re-negotiation handshake failed: Client certificate missing");
+                return FORBIDDEN;
+            }
+        }
+    }
+
+    /*
+     * Under old OpenSSL we had to change the X509_STORE inside the
+     * SSL_CTX instead inside the SSL structure, so we have to reconfigure it
+     * to the old values. This should be changed with forthcoming OpenSSL
+     * versions when better functionality is avaiable.
+     */
+#ifdef SSL_EXPERIMENTAL_PERDIRCA
+    if (renegotiate && reconfigured_locations) {
+        if (!SSL_CTX_load_verify_locations(ctx,
+                sc->szCACertificateFile, sc->szCACertificatePath)) {
+            ssl_log(r->server, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+                    "Unable to reconfigure verify locations "
+                    "to per-server configuration parameters");
+            return FORBIDDEN;
+        }
+    }
+#endif /* SSL_EXPERIMENTAL_PERDIRCA */
+
+    /*
+     * Check SSLRequire boolean expressions
+     */
+    apRequirement = dc->aRequirement;
+    pRequirements = (ssl_require_t *)apRequirement->elts;
+    for (i = 0; i < apRequirement->nelts; i++) {
+        pRequirement = &pRequirements[i];
+        ok = ssl_expr_exec(r, pRequirement->mpExpr);
+        if (ok < 0) {
+            cp = ap_psprintf(r->pool, "Failed to execute SSL requirement expression: %s",
+                             ssl_expr_get_error());
+            ap_log_reason(cp, r->filename, r);
+            /* remember forbidden access for strict require option */
+            ap_table_setn(r->notes, "ssl-access-forbidden", (void *)1);
+            return FORBIDDEN;
+        }
+        if (ok != 1) {
+            ssl_log(r->server, SSL_LOG_INFO,
+                    "Access to %s denied for %s (requirement expression not fulfilled)",
+                    r->filename, r->connection->remote_ip);
+            ssl_log(r->server, SSL_LOG_INFO,
+                    "Failed expression: %s", pRequirement->cpExpr);
+            ap_log_reason("SSL requirement expression not fulfilled "
+                          "(see SSL logfile for more details)", r->filename, r);
+            /* remember forbidden access for strict require option */
+            ap_table_setn(r->notes, "ssl-access-forbidden", (void *)1);
+            return FORBIDDEN;
+        }
+    }
+
+    /*
+     * Else access is granted from our point of view (except vendor
+     * handlers override). But we have to return DECLINED here instead
+     * of OK, because mod_auth and other modules still might want to
+     * deny access.
+     */
+    rc = DECLINED;
+#ifdef SSL_VENDOR
+    ap_hook_use("ap::mod_ssl::vendor::access_handler",
+                AP_HOOK_SIG2(int,ptr), AP_HOOK_DECLINE(DECLINED),
+                &rc, r);
+#endif
+    return rc;
+}
+
+/*
+ *  Auth Handler:
+ *  Fake a Basic authentication from the X509 client certificate.
+ *
+ *  This must be run fairly early on to prevent a real authentication from
+ *  occuring, in particular it must be run before anything else that
+ *  authenticates a user.  This means that the Module statement for this
+ *  module should be LAST in the Configuration file.
+ */
+int ssl_hook_Auth(request_rec *r)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(r->server);
+    SSLDirConfigRec *dc = myDirConfig(r);
+    char b1[MAX_STRING_LEN], b2[MAX_STRING_LEN];
+    char *clientdn;
+    const char *cpAL;
+    const char *cpUN;
+    const char *cpPW;
+
+    /*
+     * Additionally forbid access (again)
+     * when strict require option is used.
+     */
+    if (   (dc->nOptions & SSL_OPT_STRICTREQUIRE)
+        && (ap_table_get(r->notes, "ssl-access-forbidden") != NULL))
+        return FORBIDDEN;
+
+    /*
+     * Make sure the user is not able to fake the client certificate
+     * based authentication by just entering an X.509 Subject DN
+     * ("/XX=YYY/XX=YYY/..") as the username and "password" as the
+     * password.
+     */
+    if ((cpAL = ap_table_get(r->headers_in, "Authorization")) != NULL) {
+        if (strcEQ(ap_getword(r->pool, &cpAL, ' '), "Basic")) {
+            while (*cpAL == ' ' || *cpAL == '\t')
+                cpAL++;
+            cpAL = ap_pbase64decode(r->pool, cpAL);
+            cpUN = ap_getword_nulls(r->pool, &cpAL, ':');
+            cpPW = cpAL;
+            if (cpUN[0] == '/' && strEQ(cpPW, "password"))
+                return FORBIDDEN;
+        }
+    }
+
+    /*
+     * We decline operation in various situations...
+     */
+    if (!sc->bEnabled)
+        return DECLINED;
+    if (ap_ctx_get(r->connection->client->ctx, "ssl") == NULL)
+        return DECLINED;
+    if (!(dc->nOptions & SSL_OPT_FAKEBASICAUTH))
+        return DECLINED;
+    if (r->connection->user)
+        return DECLINED;
+    if ((clientdn = (char *)ap_ctx_get(r->connection->client->ctx, "ssl::client::dn")) == NULL)
+        return DECLINED;
+
+    /*
+     * Fake a password - which one would be immaterial, as, it seems, an empty
+     * password in the users file would match ALL incoming passwords, if only
+     * we were using the standard crypt library routine. Unfortunately, OpenSSL
+     * "fixes" a "bug" in crypt and thus prevents blank passwords from
+     * working.  (IMHO what they really fix is a bug in the users of the code
+     * - failing to program correctly for shadow passwords).  We need,
+     * therefore, to provide a password. This password can be matched by
+     * adding the string "xxj31ZMTZzkVA" as the password in the user file.
+     * This is just the crypted variant of the word "password" ;-)
+     */
+    ap_snprintf(b1, sizeof(b1), "%s:password", clientdn);
+    ssl_util_uuencode(b2, b1, FALSE);
+    ap_snprintf(b1, sizeof(b1), "Basic %s", b2);
+    ap_table_set(r->headers_in, "Authorization", b1);
+    ssl_log(r->server, SSL_LOG_INFO,
+            "Faking HTTP Basic Auth header: \"Authorization: %s\"", b1);
+
+    return DECLINED;
+}
+
+int ssl_hook_UserCheck(request_rec *r)
+{
+    SSLDirConfigRec *dc = myDirConfig(r);
+
+    /*
+     * Additionally forbid access (again)
+     * when strict require option is used.
+     */
+    if (   (dc->nOptions & SSL_OPT_STRICTREQUIRE)
+        && (ap_table_get(r->notes, "ssl-access-forbidden") != NULL))
+        return FORBIDDEN;
+
+    return DECLINED;
+}
+
+/*
+ *   Fixup Handler
+ */
+
+static const char *ssl_hook_Fixup_vars[] = {
+    "SSL_VERSION_INTERFACE",
+    "SSL_VERSION_LIBRARY",
+    "SSL_PROTOCOL",
+    "SSL_CIPHER",
+    "SSL_CIPHER_EXPORT",
+    "SSL_CIPHER_USEKEYSIZE",
+    "SSL_CIPHER_ALGKEYSIZE",
+    "SSL_CLIENT_VERIFY",
+    "SSL_CLIENT_M_VERSION",
+    "SSL_CLIENT_M_SERIAL",
+    "SSL_CLIENT_V_START",
+    "SSL_CLIENT_V_END",
+    "SSL_CLIENT_S_DN",
+    "SSL_CLIENT_S_DN_C",
+    "SSL_CLIENT_S_DN_ST",
+    "SSL_CLIENT_S_DN_L",
+    "SSL_CLIENT_S_DN_O",
+    "SSL_CLIENT_S_DN_OU",
+    "SSL_CLIENT_S_DN_CN",
+    "SSL_CLIENT_S_DN_T",
+    "SSL_CLIENT_S_DN_I",
+    "SSL_CLIENT_S_DN_G",
+    "SSL_CLIENT_S_DN_S",
+    "SSL_CLIENT_S_DN_D",
+    "SSL_CLIENT_S_DN_UID",
+    "SSL_CLIENT_S_DN_Email",
+    "SSL_CLIENT_I_DN",
+    "SSL_CLIENT_I_DN_C",
+    "SSL_CLIENT_I_DN_ST",
+    "SSL_CLIENT_I_DN_L",
+    "SSL_CLIENT_I_DN_O",
+    "SSL_CLIENT_I_DN_OU",
+    "SSL_CLIENT_I_DN_CN",
+    "SSL_CLIENT_I_DN_T",
+    "SSL_CLIENT_I_DN_I",
+    "SSL_CLIENT_I_DN_G",
+    "SSL_CLIENT_I_DN_S",
+    "SSL_CLIENT_I_DN_D",
+    "SSL_CLIENT_I_DN_UID",
+    "SSL_CLIENT_I_DN_Email",
+    "SSL_CLIENT_A_KEY",
+    "SSL_CLIENT_A_SIG",
+    "SSL_SERVER_M_VERSION",
+    "SSL_SERVER_M_SERIAL",
+    "SSL_SERVER_V_START",
+    "SSL_SERVER_V_END",
+    "SSL_SERVER_S_DN",
+    "SSL_SERVER_S_DN_C",
+    "SSL_SERVER_S_DN_ST",
+    "SSL_SERVER_S_DN_L",
+    "SSL_SERVER_S_DN_O",
+    "SSL_SERVER_S_DN_OU",
+    "SSL_SERVER_S_DN_CN",
+    "SSL_SERVER_S_DN_T",
+    "SSL_SERVER_S_DN_I",
+    "SSL_SERVER_S_DN_G",
+    "SSL_SERVER_S_DN_S",
+    "SSL_SERVER_S_DN_D",
+    "SSL_SERVER_S_DN_UID",
+    "SSL_SERVER_S_DN_Email",
+    "SSL_SERVER_I_DN",
+    "SSL_SERVER_I_DN_C",
+    "SSL_SERVER_I_DN_ST",
+    "SSL_SERVER_I_DN_L",
+    "SSL_SERVER_I_DN_O",
+    "SSL_SERVER_I_DN_OU",
+    "SSL_SERVER_I_DN_CN",
+    "SSL_SERVER_I_DN_T",
+    "SSL_SERVER_I_DN_I",
+    "SSL_SERVER_I_DN_G",
+    "SSL_SERVER_I_DN_S",
+    "SSL_SERVER_I_DN_D",
+    "SSL_SERVER_I_DN_UID",
+    "SSL_SERVER_I_DN_Email",
+    "SSL_SERVER_A_KEY",
+    "SSL_SERVER_A_SIG",
+    "SSL_SESSION_ID",
+    NULL
+};
+
+int ssl_hook_Fixup(request_rec *r)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(r->server);
+    SSLDirConfigRec *dc = myDirConfig(r);
+    table *e = r->subprocess_env;
+    char *var;
+    char *val;
+    STACK_OF(X509) *sk;
+    SSL *ssl;
+    int i;
+
+    /*
+     * Check to see if SSL is on
+     */
+    if (!sc->bEnabled)
+        return DECLINED;
+    if ((ssl = ap_ctx_get(r->connection->client->ctx, "ssl")) == NULL)
+        return DECLINED;
+
+    /*
+     * Annotate the SSI/CGI environment with standard SSL information
+     */
+    /* the always present HTTPS (=HTTP over SSL) flag! */
+    ap_table_set(e, "HTTPS", "on"); 
+    /* standard SSL environment variables */
+    if (dc->nOptions & SSL_OPT_STDENVVARS) {
+        for (i = 0; ssl_hook_Fixup_vars[i] != NULL; i++) {
+            var = (char *)ssl_hook_Fixup_vars[i];
+            val = ssl_var_lookup(r->pool, r->server, r->connection, r, var);
+            if (!strIsEmpty(val))
+                ap_table_set(e, var, val);
+        }
+    }
+
+    /*
+     * On-demand bloat up the SSI/CGI environment with certificate data
+     */
+    if (dc->nOptions & SSL_OPT_EXPORTCERTDATA) {
+        val = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_SERVER_CERT");
+        ap_table_set(e, "SSL_SERVER_CERT", val);
+        val = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CLIENT_CERT");
+        ap_table_set(e, "SSL_CLIENT_CERT", val);
+        if ((sk = SSL_get_peer_cert_chain(ssl)) != NULL) {
+            for (i = 0; i < sk_X509_num(sk); i++) {
+                var = ap_psprintf(r->pool, "SSL_CLIENT_CERT_CHAIN_%d", i);
+                val = ssl_var_lookup(r->pool, r->server, r->connection, r, var);
+                if (val != NULL)
+                     ap_table_set(e, var, val);
+            }
+        }
+    }
+
+    /*
+     * On-demand bloat up the SSI/CGI environment with compat variables
+     */
+#ifdef SSL_COMPAT
+    if (dc->nOptions & SSL_OPT_COMPATENVVARS)
+        ssl_compat_variables(r);
+#endif
+
+    return DECLINED;
+}
+
+/*  _________________________________________________________________
+**
+**  OpenSSL Callback Functions
+**  _________________________________________________________________
+*/
+
+/*
+ * Handle out temporary RSA private keys on demand
+ *
+ * The background of this as the TLSv1 standard explains it:
+ *
+ * | D.1. Temporary RSA keys
+ * |
+ * |    US Export restrictions limit RSA keys used for encryption to 512
+ * |    bits, but do not place any limit on lengths of RSA keys used for
+ * |    signing operations. Certificates often need to be larger than 512
+ * |    bits, since 512-bit RSA keys are not secure enough for high-value
+ * |    transactions or for applications requiring long-term security. Some
+ * |    certificates are also designated signing-only, in which case they
+ * |    cannot be used for key exchange.
+ * |
+ * |    When the public key in the certificate cannot be used for encryption,
+ * |    the server signs a temporary RSA key, which is then exchanged. In
+ * |    exportable applications, the temporary RSA key should be the maximum
+ * |    allowable length (i.e., 512 bits). Because 512-bit RSA keys are
+ * |    relatively insecure, they should be changed often. For typical
+ * |    electronic commerce applications, it is suggested that keys be
+ * |    changed daily or every 500 transactions, and more often if possible.
+ * |    Note that while it is acceptable to use the same temporary key for
+ * |    multiple transactions, it must be signed each time it is used.
+ * |
+ * |    RSA key generation is a time-consuming process. In many cases, a
+ * |    low-priority process can be assigned the task of key generation.
+ * |    Whenever a new key is completed, the existing temporary key can be
+ * |    replaced with the new one.
+ *
+ * So we generated 512 and 1024 bit temporary keys on startup
+ * which we now just handle out on demand....
+ */
+RSA *ssl_callback_TmpRSA(SSL *pSSL, int nExport, int nKeyLen)
+{
+    SSLModConfigRec *mc = myModConfig();
+    RSA *rsa;
+
+    rsa = NULL;
+    if (nExport) {
+        /* It's because an export cipher is used */
+        if (nKeyLen == 512)
+            rsa = (RSA *)mc->pTmpKeys[SSL_TKPIDX_RSA512];
+        else if (nKeyLen == 1024)
+            rsa = (RSA *)mc->pTmpKeys[SSL_TKPIDX_RSA1024];
+        else
+            /* it's too expensive to generate on-the-fly, so keep 1024bit */
+            rsa = (RSA *)mc->pTmpKeys[SSL_TKPIDX_RSA1024];
+    }
+    else {
+        /* It's because a sign-only certificate situation exists */
+        rsa = (RSA *)mc->pTmpKeys[SSL_TKPIDX_RSA1024];
+    }
+    return rsa;
+}
+
+/* 
+ * Handle out the already generated DH parameters...
+ */
+DH *ssl_callback_TmpDH(SSL *pSSL, int nExport, int nKeyLen)
+{
+    SSLModConfigRec *mc = myModConfig();
+    DH *dh;
+
+    dh = NULL;
+    if (nExport) {
+        /* It's because an export cipher is used */
+        if (nKeyLen == 512)
+            dh = (DH *)mc->pTmpKeys[SSL_TKPIDX_DH512];
+        else if (nKeyLen == 1024)
+            dh = (DH *)mc->pTmpKeys[SSL_TKPIDX_DH1024];
+        else
+            /* it's too expensive to generate on-the-fly, so keep 1024bit */
+            dh = (DH *)mc->pTmpKeys[SSL_TKPIDX_DH1024];
+    }
+    else {
+        /* It's because a sign-only certificate situation exists */
+        dh = (DH *)mc->pTmpKeys[SSL_TKPIDX_DH1024];
+    }
+    return dh;
+}
+
+/*
+ * This OpenSSL callback function is called when OpenSSL
+ * does client authentication and verifies the certificate chain.
+ */
+int ssl_callback_SSLVerify(int ok, X509_STORE_CTX *ctx)
+{
+    SSL *ssl;
+    conn_rec *conn;
+    server_rec *s;
+    request_rec *r;
+    SSLSrvConfigRec *sc;
+    SSLDirConfigRec *dc;
+    ap_ctx *actx;
+    X509 *xs;
+    int errnum;
+    int errdepth;
+    char *cp;
+    char *cp2;
+    int depth;
+    int verify;
+
+    /*
+     * Get Apache context back through OpenSSL context
+     */
+    ssl  = (SSL *)X509_STORE_CTX_get_app_data(ctx);
+    conn = (conn_rec *)SSL_get_app_data(ssl);
+    actx = (ap_ctx *)SSL_get_app_data2(ssl);
+    r    = (request_rec *)ap_ctx_get(actx, "ssl::request_rec");
+    s    = conn->server;
+    sc   = mySrvConfig(s);
+    dc   = (r != NULL ? myDirConfig(r) : NULL);
+
+    /*
+     * Get verify ingredients
+     */
+    xs       = X509_STORE_CTX_get_current_cert(ctx);
+    errnum   = X509_STORE_CTX_get_error(ctx);
+    errdepth = X509_STORE_CTX_get_error_depth(ctx);
+
+    /*
+     * Log verification information
+     */
+    cp  = X509_NAME_oneline(X509_get_subject_name(xs), NULL, 0);
+    cp2 = X509_NAME_oneline(X509_get_issuer_name(xs),  NULL, 0);
+    ssl_log(s, SSL_LOG_TRACE,
+            "Certificate Verification: depth: %d, subject: %s, issuer: %s",
+            errdepth, cp != NULL ? cp : "-unknown-",
+            cp2 != NULL ? cp2 : "-unknown");
+    if (cp)
+        free(cp);
+    if (cp2)
+        free(cp2);
+
+    /*
+     * Check for optionally acceptable non-verifiable issuer situation
+     */
+    if (dc != NULL && dc->nVerifyClient != SSL_CVERIFY_UNSET)
+        verify = dc->nVerifyClient;
+    else
+        verify = sc->nVerifyClient;
+    if (   (   errnum == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT
+            || errnum == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN
+            || errnum == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY
+#if SSL_LIBRARY_VERSION >= 0x00905000
+            || errnum == X509_V_ERR_CERT_UNTRUSTED
+#endif
+            || errnum == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE  )
+        && verify == SSL_CVERIFY_OPTIONAL_NO_CA                       ) {
+        ssl_log(s, SSL_LOG_TRACE,
+                "Certificate Verification: Verifiable Issuer is configured as "
+                "optional, therefore we're accepting the certificate");
+        ap_ctx_set(conn->client->ctx, "ssl::verify::info", "GENEROUS");
+        ok = TRUE;
+    }
+
+    /*
+     * Additionally perform CRL-based revocation checks
+     */
+    if (ok) {
+        ok = ssl_callback_SSLVerify_CRL(ok, ctx, s);
+        if (!ok)
+            errnum = X509_STORE_CTX_get_error(ctx);
+    }
+
+    /*
+     * If we already know it's not ok, log the real reason
+     */
+    if (!ok) {
+        ssl_log(s, SSL_LOG_ERROR, "Certificate Verification: Error (%d): %s",
+                errnum, X509_verify_cert_error_string(errnum));
+        ap_ctx_set(conn->client->ctx, "ssl::client::dn", NULL);
+        ap_ctx_set(conn->client->ctx, "ssl::verify::error",
+                   (void *)X509_verify_cert_error_string(errnum));
+    }
+
+    /*
+     * Finally check the depth of the certificate verification
+     */
+    if (dc != NULL && dc->nVerifyDepth != UNSET)
+        depth = dc->nVerifyDepth;
+    else
+        depth = sc->nVerifyDepth;
+    if (errdepth > depth) {
+        ssl_log(s, SSL_LOG_ERROR,
+                "Certificate Verification: Certificate Chain too long "
+                "(chain has %d certificates, but maximum allowed are only %d)",
+                errdepth, depth);
+        ap_ctx_set(conn->client->ctx, "ssl::verify::error",
+                   (void *)X509_verify_cert_error_string(X509_V_ERR_CERT_CHAIN_TOO_LONG));
+        ok = FALSE;
+    }
+
+    /*
+     * And finally signal OpenSSL the (perhaps changed) state
+     */
+    return (ok);
+}
+
+int ssl_callback_SSLVerify_CRL(
+    int ok, X509_STORE_CTX *ctx, server_rec *s)
+{
+    SSLSrvConfigRec *sc;
+    X509_OBJECT obj;
+    X509_NAME *subject;
+    X509_NAME *issuer;
+    X509 *xs;
+    X509_CRL *crl;
+    X509_REVOKED *revoked;
+    long serial;
+    BIO *bio;
+    int i, n, rc;
+    char *cp;
+    char *cp2;
+
+    /*
+     * Unless a revocation store for CRLs was created we
+     * cannot do any CRL-based verification, of course.
+     */
+    sc = mySrvConfig(s);
+    if (sc->pRevocationStore == NULL)
+        return ok;
+
+    /*
+     * Determine certificate ingredients in advance
+     */
+    xs      = X509_STORE_CTX_get_current_cert(ctx);
+    subject = X509_get_subject_name(xs);
+    issuer  = X509_get_issuer_name(xs);
+
+    /*
+     * OpenSSL provides the general mechanism to deal with CRLs but does not
+     * use them automatically when verifying certificates, so we do it
+     * explicitly here. We will check the CRL for the currently checked
+     * certificate, if there is such a CRL in the store.
+     *
+     * We come through this procedure for each certificate in the certificate
+     * chain, starting with the root-CA's certificate. At each step we've to
+     * both verify the signature on the CRL (to make sure it's a valid CRL)
+     * and it's revocation list (to make sure the current certificate isn't
+     * revoked).  But because to check the signature on the CRL we need the
+     * public key of the issuing CA certificate (which was already processed
+     * one round before), we've a little problem. But we can both solve it and
+     * at the same time optimize the processing by using the following
+     * verification scheme (idea and code snippets borrowed from the GLOBUS
+     * project):
+     *
+     * 1. We'll check the signature of a CRL in each step when we find a CRL
+     *    through the _subject_ name of the current certificate. This CRL
+     *    itself will be needed the first time in the next round, of course.
+     *    But we do the signature processing one round before this where the
+     *    public key of the CA is available.
+     *
+     * 2. We'll check the revocation list of a CRL in each step when
+     *    we find a CRL through the _issuer_ name of the current certificate.
+     *    This CRLs signature was then already verified one round before.
+     *
+     * This verification scheme allows a CA to revoke its own certificate as
+     * well, of course.
+     */
+
+    /*
+     * Try to retrieve a CRL corresponding to the _subject_ of
+     * the current certificate in order to verify it's integrity.
+     */
+    memset((char *)&obj, 0, sizeof(obj));
+    rc = SSL_X509_STORE_lookup(sc->pRevocationStore, X509_LU_CRL, subject, &obj);
+    crl = obj.data.crl;
+    if (rc > 0 && crl != NULL) {
+        /*
+         * Log information about CRL
+         * (A little bit complicated because of ASN.1 and BIOs...)
+         */
+        if (ssl_log_applies(s, SSL_LOG_TRACE)) {
+            bio = BIO_new(BIO_s_mem());
+            BIO_printf(bio, "lastUpdate: ");
+            ASN1_UTCTIME_print(bio, X509_CRL_get_lastUpdate(crl));
+            BIO_printf(bio, ", nextUpdate: ");
+            ASN1_UTCTIME_print(bio, X509_CRL_get_nextUpdate(crl));
+            n = BIO_pending(bio);
+            cp = malloc(n+1);
+            n = BIO_read(bio, cp, n);
+            cp[n] = NUL;
+            BIO_free(bio);
+            cp2 = X509_NAME_oneline(subject, NULL, 0);
+            ssl_log(s, SSL_LOG_TRACE, "CA CRL: Issuer: %s, %s", cp2, cp);
+            free(cp2);
+            free(cp);
+        }
+
+        /*
+         * Verify the signature on this CRL
+         */
+        if (X509_CRL_verify(crl, X509_get_pubkey(xs)) <= 0) {
+            ssl_log(s, SSL_LOG_WARN, "Invalid signature on CRL");
+            X509_STORE_CTX_set_error(ctx, X509_V_ERR_CRL_SIGNATURE_FAILURE);
+            X509_OBJECT_free_contents(&obj);
+            return FALSE;
+        }
+
+        /*
+         * Check date of CRL to make sure it's not expired
+         */
+        i = X509_cmp_current_time(X509_CRL_get_nextUpdate(crl));
+        if (i == 0) {
+            ssl_log(s, SSL_LOG_WARN, "Found CRL has invalid nextUpdate field");
+            X509_STORE_CTX_set_error(ctx, X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD);
+            X509_OBJECT_free_contents(&obj);
+            return FALSE;
+        }
+        if (i < 0) {
+            ssl_log(s, SSL_LOG_WARN,
+                    "Found CRL is expired - "
+                    "revoking all certificates until you get updated CRL");
+            X509_STORE_CTX_set_error(ctx, X509_V_ERR_CRL_HAS_EXPIRED);
+            X509_OBJECT_free_contents(&obj);
+            return FALSE;
+        }
+        X509_OBJECT_free_contents(&obj);
+    }
+
+    /*
+     * Try to retrieve a CRL corresponding to the _issuer_ of
+     * the current certificate in order to check for revocation.
+     */
+    memset((char *)&obj, 0, sizeof(obj));
+    rc = SSL_X509_STORE_lookup(sc->pRevocationStore, X509_LU_CRL, issuer, &obj);
+    crl = obj.data.crl;
+    if (rc > 0 && crl != NULL) {
+        /*
+         * Check if the current certificate is revoked by this CRL
+         */
+#if SSL_LIBRARY_VERSION < 0x00904000
+        n = sk_num(X509_CRL_get_REVOKED(crl));
+#else
+        n = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl));
+#endif
+        for (i = 0; i < n; i++) {
+#if SSL_LIBRARY_VERSION < 0x00904000
+            revoked = (X509_REVOKED *)sk_value(X509_CRL_get_REVOKED(crl), i);
+#else
+            revoked = sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i);
+#endif
+            if (ASN1_INTEGER_cmp(revoked->serialNumber, X509_get_serialNumber(xs)) == 0) {
+
+                serial = ASN1_INTEGER_get(revoked->serialNumber);
+                cp = X509_NAME_oneline(issuer, NULL, 0);
+                ssl_log(s, SSL_LOG_INFO,
+                        "Certificate with serial %ld (0x%lX) "
+                        "revoked per CRL from issuer %s",
+                        serial, serial, cp);
+                free(cp);
+
+                X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_REVOKED);
+                X509_OBJECT_free_contents(&obj);
+                return FALSE;
+            }
+        }
+        X509_OBJECT_free_contents(&obj);
+    }
+    return ok;
+}
+
+/*
+ *  This callback function is executed by OpenSSL whenever a new SSL_SESSION is
+ *  added to the internal OpenSSL session cache. We use this hook to spread the
+ *  SSL_SESSION also to the inter-process disk-cache to make share it with our
+ *  other Apache pre-forked server processes.
+ */
+int ssl_callback_NewSessionCacheEntry(SSL *ssl, SSL_SESSION *pNew)
+{
+    conn_rec *conn;
+    server_rec *s;
+    SSLSrvConfigRec *sc;
+    long t;
+    BOOL rc;
+
+    /*
+     * Get Apache context back through OpenSSL context
+     */
+    conn = (conn_rec *)SSL_get_app_data(ssl);
+    s    = conn->server;
+    sc   = mySrvConfig(s);
+
+    /*
+     * Set the timeout also for the internal OpenSSL cache, because this way
+     * our inter-process cache is consulted only when it's really necessary.
+     */
+    t = sc->nSessionCacheTimeout;
+    SSL_set_timeout(pNew, t);
+
+    /*
+     * Store the SSL_SESSION in the inter-process cache with the
+     * same expire time, so it expires automatically there, too.
+     */
+    t = (SSL_get_time(pNew) + sc->nSessionCacheTimeout);
+    rc = ssl_scache_store(s, pNew->session_id, pNew->session_id_length, t, pNew);
+
+    /*
+     * Log this cache operation
+     */
+    ssl_log(s, SSL_LOG_TRACE, "Inter-Process Session Cache: "
+            "request=SET status=%s id=%s timeout=%ds (session caching)",
+            rc == TRUE ? "OK" : "BAD",
+            SSL_SESSION_id2sz(pNew->session_id, pNew->session_id_length),
+            t-time(NULL));
+
+    /*
+     * return 0 which means to OpenSSL that the pNew is still
+     * valid and was not freed by us with SSL_SESSION_free().
+     */
+    return 0;
+}
+
+/*
+ *  This callback function is executed by OpenSSL whenever a
+ *  SSL_SESSION is looked up in the internal OpenSSL cache and it
+ *  was not found. We use this to lookup the SSL_SESSION in the
+ *  inter-process disk-cache where it was perhaps stored by one
+ *  of our other Apache pre-forked server processes.
+ */
+SSL_SESSION *ssl_callback_GetSessionCacheEntry(
+    SSL *ssl, unsigned char *id, int idlen, int *pCopy)
+{
+    conn_rec *conn;
+    server_rec *s;
+    SSL_SESSION *pSession;
+
+    /*
+     * Get Apache context back through OpenSSL context
+     */
+    conn = (conn_rec *)SSL_get_app_data(ssl);
+    s    = conn->server;
+
+    /*
+     * Try to retrieve the SSL_SESSION from the inter-process cache
+     */
+    pSession = ssl_scache_retrieve(s, id, idlen);
+
+    /*
+     * Log this cache operation
+     */
+    if (pSession != NULL)
+        ssl_log(s, SSL_LOG_TRACE, "Inter-Process Session Cache: "
+                "request=GET status=FOUND id=%s (session reuse)",
+                SSL_SESSION_id2sz(id, idlen));
+    else
+        ssl_log(s, SSL_LOG_TRACE, "Inter-Process Session Cache: "
+                "request=GET status=MISSED id=%s (session renewal)",
+                SSL_SESSION_id2sz(id, idlen));
+
+    /*
+     * Return NULL or the retrieved SSL_SESSION. But indicate (by
+     * setting pCopy to 0) that the reference count on the
+     * SSL_SESSION should not be incremented by the SSL library,
+     * because we will no longer hold a reference to it ourself.
+     */
+    *pCopy = 0;
+    return pSession;
+}
+
+/*
+ *  This callback function is executed by OpenSSL whenever a
+ *  SSL_SESSION is removed from the the internal OpenSSL cache.
+ *  We use this to remove the SSL_SESSION in the inter-process
+ *  disk-cache, too.
+ */
+void ssl_callback_DelSessionCacheEntry(
+    SSL_CTX *ctx, SSL_SESSION *pSession)
+{
+    server_rec *s;
+
+    /*
+     * Get Apache context back through OpenSSL context
+     */
+    s = (server_rec *)SSL_CTX_get_app_data(ctx);
+    if (s == NULL) /* on server shutdown Apache is already gone */
+        return;
+
+    /*
+     * Remove the SSL_SESSION from the inter-process cache
+     */
+    ssl_scache_remove(s, pSession->session_id, pSession->session_id_length);
+
+    /*
+     * Log this cache operation
+     */
+    ssl_log(s, SSL_LOG_TRACE, "Inter-Process Session Cache: "
+            "request=REM status=OK id=%s (session dead)",
+            SSL_SESSION_id2sz(pSession->session_id,
+            pSession->session_id_length));
+
+    return;
+}
+
+/*
+ * This callback function is executed while OpenSSL processes the
+ * SSL handshake and does SSL record layer stuff. We use it to
+ * trace OpenSSL's processing in out SSL logfile.
+ */
+void ssl_callback_LogTracingState(SSL *ssl, int where, int rc)
+{
+    conn_rec *c;
+    server_rec *s;
+    SSLSrvConfigRec *sc;
+    char *str;
+
+    /*
+     * find corresponding server
+     */
+    if ((c = (conn_rec *)SSL_get_app_data(ssl)) == NULL)
+        return;
+    s = c->server;
+    if ((sc = mySrvConfig(s)) == NULL)
+        return;
+
+    /*
+     * create the various trace messages
+     */
+    if (sc->nLogLevel >= SSL_LOG_TRACE) {
+        if (where & SSL_CB_HANDSHAKE_START)
+            ssl_log(s, SSL_LOG_TRACE, "%s: Handshake: start", SSL_LIBRARY_NAME);
+        else if (where & SSL_CB_HANDSHAKE_DONE)
+            ssl_log(s, SSL_LOG_TRACE, "%s: Handshake: done", SSL_LIBRARY_NAME);
+        else if (where & SSL_CB_LOOP)
+            ssl_log(s, SSL_LOG_TRACE, "%s: Loop: %s",
+                    SSL_LIBRARY_NAME, SSL_state_string_long(ssl));
+        else if (where & SSL_CB_READ)
+            ssl_log(s, SSL_LOG_TRACE, "%s: Read: %s",
+                    SSL_LIBRARY_NAME, SSL_state_string_long(ssl));
+        else if (where & SSL_CB_WRITE)
+            ssl_log(s, SSL_LOG_TRACE, "%s: Write: %s",
+                    SSL_LIBRARY_NAME, SSL_state_string_long(ssl));
+        else if (where & SSL_CB_ALERT) {
+            str = (where & SSL_CB_READ) ? "read" : "write";
+            ssl_log(s, SSL_LOG_TRACE, "%s: Alert: %s:%s:%s\n",
+                    SSL_LIBRARY_NAME, str,
+                    SSL_alert_type_string_long(rc),
+                    SSL_alert_desc_string_long(rc));
+        }
+        else if (where & SSL_CB_EXIT) {
+            if (rc == 0)
+                ssl_log(s, SSL_LOG_TRACE, "%s: Exit: failed in %s",
+                        SSL_LIBRARY_NAME, SSL_state_string_long(ssl));
+            else if (rc < 0)
+                ssl_log(s, SSL_LOG_TRACE, "%s: Exit: error in %s",
+                        SSL_LIBRARY_NAME, SSL_state_string_long(ssl));
+        }
+    }
+
+    /*
+     * Because SSL renegotations can happen at any time (not only after
+     * SSL_accept()), the best way to log the current connection details is
+     * right after a finished handshake.
+     */
+    if (where & SSL_CB_HANDSHAKE_DONE) {
+        ssl_log(s, SSL_LOG_INFO,
+                "Connection: Client IP: %s, Protocol: %s, Cipher: %s (%s/%s bits)",
+                ssl_var_lookup(NULL, s, c, NULL, "REMOTE_ADDR"),
+                ssl_var_lookup(NULL, s, c, NULL, "SSL_PROTOCOL"),
+                ssl_var_lookup(NULL, s, c, NULL, "SSL_CIPHER"),
+                ssl_var_lookup(NULL, s, c, NULL, "SSL_CIPHER_USEKEYSIZE"),
+                ssl_var_lookup(NULL, s, c, NULL, "SSL_CIPHER_ALGKEYSIZE"));
+    }
+
+    return;
+}
+
diff --git a/modules/ssl/ssl_engine_log.c b/modules/ssl/ssl_engine_log.c
new file mode 100644 (file)
index 0000000..0e1c53a
--- /dev/null
@@ -0,0 +1,326 @@
+/*                      _             _
+**  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
+** | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
+** | | | | | | (_) | (_| |   \__ \__ \ |  www.modssl.org
+** |_| |_| |_|\___/ \__,_|___|___/___/_|  ftp.modssl.org
+**                      |_____|
+**  ssl_engine_log.c
+**  Logging Facility
+*/
+
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ *    nor may "mod_ssl" appear in their names without prior
+ *    written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+                             /* ``The difference between a computer
+                                  industry job and open-source software
+                                  hacking is about 30 hours a week.''
+                                         -- Ralf S. Engelschall     */
+#include "mod_ssl.h"
+
+
+/*  _________________________________________________________________
+**
+**  Logfile Support
+**  _________________________________________________________________
+*/
+
+/*
+ * Open the SSL logfile
+ */
+void ssl_log_open(server_rec *s_main, server_rec *s, pool *p)
+{
+    char *szLogFile;
+    SSLSrvConfigRec *sc_main = mySrvConfig(s_main);
+    SSLSrvConfigRec *sc = mySrvConfig(s);
+    piped_log *pl;
+
+    /* 
+     * Short-circuit for inherited logfiles in order to save
+     * filedescriptors in mass-vhost situation. Be careful, this works
+     * fine because the close happens implicitly by the pool facility.
+     */
+    if (   s != s_main 
+        && sc_main->fileLogFile != NULL
+        && (   (sc->szLogFile == NULL)
+            || (   sc->szLogFile != NULL 
+                && sc_main->szLogFile != NULL 
+                && strEQ(sc->szLogFile, sc_main->szLogFile)))) {
+        sc->fileLogFile = sc_main->fileLogFile;
+    }
+    else if (sc->szLogFile != NULL) {
+        if (strEQ(sc->szLogFile, "/dev/null"))
+            return;
+        else if (sc->szLogFile[0] == '|') {
+            szLogFile = ssl_util_server_root_relative(p, "log", sc->szLogFile+1);
+            if ((pl = ap_open_piped_log(p, szLogFile)) == NULL) {
+                ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO,
+                        "Cannot open reliable pipe to SSL logfile filter %s", szLogFile);
+                ssl_die();
+            }
+            sc->fileLogFile = ap_pfdopen(p, ap_piped_log_write_fd(pl), "a");
+            setbuf(sc->fileLogFile, NULL);
+        }
+        else {
+            szLogFile = ssl_util_server_root_relative(p, "log", sc->szLogFile);
+            if ((sc->fileLogFile = ap_pfopen(p, szLogFile, "a")) == NULL) {
+                ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO,
+                        "Cannot open SSL logfile %s", szLogFile);
+                ssl_die();
+            }
+            setbuf(sc->fileLogFile, NULL);
+        }
+    }
+    return;
+}
+
+static struct {
+    int   nLevel;
+    char *szLevel;
+} ssl_log_level2string[] = {
+    { SSL_LOG_ERROR, "error" },
+    { SSL_LOG_WARN,  "warn"  },
+    { SSL_LOG_INFO,  "info"  },
+    { SSL_LOG_TRACE, "trace" },
+    { SSL_LOG_DEBUG, "debug" },
+    { 0, NULL }
+};
+
+static struct {
+    char *cpPattern;
+    char *cpAnnotation;
+} ssl_log_annotate[] = {
+    { "*envelope*bad*decrypt*", "wrong pass phrase!?" },
+    { "*CLIENT_HELLO*unknown*protocol*", "speaking not SSL to HTTPS port!?" },
+    { "*CLIENT_HELLO*http*request*", "speaking HTTP to HTTPS port!?" },
+    { "*SSL3_READ_BYTES:sslv3*alert*bad*certificate*", "Subject CN in certificate not server name or identical to CA!?" },
+    { "*self signed certificate in certificate chain*", "Client certificate signed by CA not known to server?" },
+    { "*peer did not return a certificate*", "No CAs known to server for verification?" },
+    { "*no shared cipher*", "Too restrictive SSLCipherSuite or using DSA server certificate?" },
+    { "*no start line*", "Bad file contents or format - or even just a forgotten SSLCertificateKeyFile?" },
+    { "*bad password read*", "You entered an incorrect pass phrase!?" },
+    { "*bad mac decode*", "Browser still remembered details of a re-created server certificate?" },
+    { NULL, NULL }
+};
+
+static char *ssl_log_annotation(char *error)
+{
+    char *errstr;
+    int i;
+
+    errstr = NULL;
+    for (i = 0; ssl_log_annotate[i].cpPattern != NULL; i++) {
+        if (ap_strcmp_match(error, ssl_log_annotate[i].cpPattern) == 0) {
+            errstr = ssl_log_annotate[i].cpAnnotation;
+            break;
+        }
+    }
+    return errstr;
+}
+
+BOOL ssl_log_applies(server_rec *s, int level)
+{
+    SSLSrvConfigRec *sc;
+
+    sc = mySrvConfig(s);
+    if (   sc->fileLogFile == NULL
+        && !(level & SSL_LOG_ERROR))
+        return FALSE;
+    if (   level > sc->nLogLevel
+        && !(level & SSL_LOG_ERROR))
+        return FALSE;
+    return TRUE;
+}
+
+void ssl_log(server_rec *s, int level, const char *msg, ...)
+{
+    char tstr[80];
+    char lstr[20];
+    char vstr[1024];
+    char str[1024];
+    char nstr[2];
+    int timz;
+    struct tm *t;
+    va_list ap;
+    int add;
+    int i;
+    char *astr;
+    int safe_errno;
+    unsigned long e;
+    SSLSrvConfigRec *sc;
+    char *cpE;
+    char *cpA;
+
+    /*  initialization  */
+    va_start(ap, msg);
+    safe_errno = errno;
+    sc = mySrvConfig(s);
+
+    /*  strip out additional flags  */
+    add   = (level & ~SSL_LOG_MASK);
+    level = (level & SSL_LOG_MASK);
+
+    /*  reduce flags when not reasonable in context  */
+    if (add & SSL_ADD_ERRNO && errno == 0)
+        add &= ~SSL_ADD_ERRNO;
+    if (add & SSL_ADD_SSLERR && ERR_peek_error() == 0)
+        add &= ~SSL_ADD_SSLERR;
+
+    /*  we log only levels below, except for errors */
+    if (   sc->fileLogFile == NULL
+        && !(level & SSL_LOG_ERROR))
+        return;
+    if (   level > sc->nLogLevel
+        && !(level & SSL_LOG_ERROR))
+        return;
+
+    /*  determine the time entry string  */
+    if (add & SSL_NO_TIMESTAMP)
+        tstr[0] = NUL;
+    else {
+        t = ap_get_gmtoff(&timz);
+        strftime(tstr, 80, "[%d/%b/%Y %H:%M:%S", t);
+        i = strlen(tstr);
+        ap_snprintf(tstr+i, 80-i, " %05d] ", (unsigned int)getpid());
+    }
+
+    /*  determine whether newline should be written */
+    if (add & SSL_NO_NEWLINE)
+        nstr[0] = NUL;
+    else {
+        nstr[0] = '\n';
+        nstr[1] = NUL;
+    }
+
+    /*  determine level name  */
+    lstr[0] = NUL;
+    if (!(add & SSL_NO_LEVELID)) {
+        for (i = 0; ssl_log_level2string[i].nLevel != 0; i++) {
+            if (ssl_log_level2string[i].nLevel == level) {
+                ap_snprintf(lstr, sizeof(lstr), "[%s]", ssl_log_level2string[i].szLevel);
+                break;
+            }
+        }
+        for (i = strlen(lstr); i <= 7; i++)
+            lstr[i] = ' ';
+        lstr[i] = NUL;
+    }
+
+    /*  create custom message  */
+    ap_vsnprintf(vstr, sizeof(vstr), msg, ap);
+
+    /*  write out SSLog message  */
+    if ((add & SSL_ADD_ERRNO) && (add & SSL_ADD_SSLERR))
+        astr = " (System and " SSL_LIBRARY_NAME " library errors follow)";
+    else if (add & SSL_ADD_ERRNO)
+        astr = " (System error follows)";
+    else if (add & SSL_ADD_SSLERR)
+        astr = " (" SSL_LIBRARY_NAME " library error follows)";
+    else
+        astr = "";
+    if (level <= sc->nLogLevel && sc->fileLogFile != NULL) {
+        ap_snprintf(str, sizeof(str), "%s%s%s%s%s", tstr, lstr, vstr, astr, nstr);
+        fprintf(sc->fileLogFile, "%s", str);
+    }
+    if (level & SSL_LOG_ERROR)
+        ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, s,
+                     "mod_ssl: %s%s", vstr, astr);
+
+    /*  write out additional attachment messages  */
+    if (add & SSL_ADD_ERRNO) {
+        if (level <= sc->nLogLevel && sc->fileLogFile != NULL) {
+            ap_snprintf(str, sizeof(str), "%s%sSystem: %s (errno: %d)%s",
+                        tstr, lstr, strerror(safe_errno), safe_errno, nstr);
+            fprintf(sc->fileLogFile, "%s", str);
+        }
+        if (level & SSL_LOG_ERROR)
+            ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, s,
+                         "System: %s (errno: %d)",
+                         strerror(safe_errno), safe_errno);
+    }
+    if (add & SSL_ADD_SSLERR) {
+        while ((e = ERR_get_error())) {
+            cpE = ERR_error_string(e, NULL);
+            cpA = ssl_log_annotation(cpE);
+            if (level <= sc->nLogLevel && sc->fileLogFile != NULL) {
+                ap_snprintf(str, sizeof(str), "%s%s%s: %s%s%s%s%s",
+                            tstr, lstr, SSL_LIBRARY_NAME, cpE,
+                            cpA != NULL ? " [Hint: " : "",
+                            cpA != NULL ? cpA : "", cpA != NULL ? "]" : "",
+                            nstr);
+                fprintf(sc->fileLogFile, "%s", str);
+            }
+            if (level & SSL_LOG_ERROR)
+                ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, s,
+                             "%s: %s%s%s%s", SSL_LIBRARY_NAME, cpE,
+                             cpA != NULL ? " [Hint: " : "",
+                             cpA != NULL ? cpA : "", cpA != NULL ? "]" : "");
+        }
+    }
+    /* make sure the next log starts from a clean base */
+    /* ERR_clear_error(); */
+
+    /*  cleanup and return  */
+    if (sc->fileLogFile != NULL)
+        fflush(sc->fileLogFile);
+    errno = safe_errno;
+    va_end(ap);
+    return;
+}
+
+void ssl_die(void)
+{
+    /*
+     * This is used for fatal errors and here
+     * it is common module practice to really
+     * exit from the complete program.
+     */
+    exit(1);
+}
+
diff --git a/modules/ssl/ssl_engine_mutex.c b/modules/ssl/ssl_engine_mutex.c
new file mode 100644 (file)
index 0000000..146f9ce
--- /dev/null
@@ -0,0 +1,397 @@
+/*                      _             _
+**  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
+** | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
+** | | | | | | (_) | (_| |   \__ \__ \ |  www.modssl.org
+** |_| |_| |_|\___/ \__,_|___|___/___/_|  ftp.modssl.org
+**                      |_____|
+**  ssl_engine_mutex.c
+**  Semaphore for Mutual Exclusion
+*/
+
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ *    nor may "mod_ssl" appear in their names without prior
+ *    written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+                             /* ``Real programmers confuse
+                                  Christmas and Halloween
+                                  because DEC 25 = OCT 31.''
+                                             -- Unknown     */
+#include "mod_ssl.h"
+
+
+/*  _________________________________________________________________
+**
+**  Mutex Support (Common)
+**  _________________________________________________________________
+*/
+
+void ssl_mutex_init(server_rec *s, pool *p)
+{
+    SSLModConfigRec *mc = myModConfig();
+
+    if (mc->nMutexMode == SSL_MUTEXMODE_FILE)
+        ssl_mutex_file_create(s, p);
+    else if (mc->nMutexMode == SSL_MUTEXMODE_SEM)
+        ssl_mutex_sem_create(s, p);
+    return;
+}
+
+void ssl_mutex_reinit(server_rec *s, pool *p)
+{
+    SSLModConfigRec *mc = myModConfig();
+
+    if (mc->nMutexMode == SSL_MUTEXMODE_FILE)
+        ssl_mutex_file_open(s, p);
+    else if (mc->nMutexMode == SSL_MUTEXMODE_SEM)
+        ssl_mutex_sem_open(s, p);
+    return;
+}
+
+void ssl_mutex_on(server_rec *s)
+{
+    SSLModConfigRec *mc = myModConfig();
+    BOOL ok = TRUE;
+
+    if (mc->nMutexMode == SSL_MUTEXMODE_FILE)
+        ok = ssl_mutex_file_acquire();
+    else if (mc->nMutexMode == SSL_MUTEXMODE_SEM)
+        ok = ssl_mutex_sem_acquire();
+    if (!ok)
+        ssl_log(s, SSL_LOG_WARN, "Failed to acquire global mutex lock");
+    return;
+}
+
+void ssl_mutex_off(server_rec *s)
+{
+    SSLModConfigRec *mc = myModConfig();
+    BOOL ok = TRUE;
+
+    if (mc->nMutexMode == SSL_MUTEXMODE_FILE)
+        ok = ssl_mutex_file_release();
+    else if (mc->nMutexMode == SSL_MUTEXMODE_SEM)
+        ok = ssl_mutex_sem_release();
+    if (!ok)
+        ssl_log(s, SSL_LOG_WARN, "Failed to release global mutex lock");
+    return;
+}
+
+void ssl_mutex_kill(server_rec *s)
+{
+    SSLModConfigRec *mc = myModConfig();
+
+    if (mc->nMutexMode == SSL_MUTEXMODE_FILE)
+        ssl_mutex_file_remove(s);
+    else if (mc->nMutexMode == SSL_MUTEXMODE_SEM)
+        ssl_mutex_sem_remove(s);
+    return;
+}
+
+
+/*  _________________________________________________________________
+**
+**  Mutex Support (Lockfile)
+**  _________________________________________________________________
+*/
+
+void ssl_mutex_file_create(server_rec *s, pool *p)
+{
+#ifndef WIN32
+    SSLModConfigRec *mc = myModConfig();
+
+    /* create the lockfile */
+    unlink(mc->szMutexFile);
+    if ((mc->nMutexFD = ap_popenf(p, mc->szMutexFile,
+                                  O_WRONLY|O_CREAT, SSL_MUTEX_LOCK_MODE)) < 0) {
+        ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO,
+                "Parent process could not create SSLMutex lockfile %s",
+                mc->szMutexFile);
+        ssl_die();
+    }
+    ap_pclosef(p, mc->nMutexFD);
+
+    /* make sure the childs have access to this file */
+#ifndef OS2
+    if (geteuid() == 0 /* is superuser */)
+        chown(mc->szMutexFile, ap_user_id, -1 /* no gid change */);
+#endif
+
+    /* open the lockfile for real */
+    if ((mc->nMutexFD = ap_popenf(p, mc->szMutexFile,
+                                  O_WRONLY, SSL_MUTEX_LOCK_MODE)) < 0) {
+        ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO,
+                "Parent could not open SSLMutex lockfile %s",
+                mc->szMutexFile);
+        ssl_die();
+    }
+#endif
+    return;
+}
+
+void ssl_mutex_file_open(server_rec *s, pool *p)
+{
+#ifndef WIN32
+    SSLModConfigRec *mc = myModConfig();
+
+    /* open the lockfile (once per child) to get a unique fd */
+    if ((mc->nMutexFD = ap_popenf(p, mc->szMutexFile,
+                                  O_WRONLY, SSL_MUTEX_LOCK_MODE)) < 0) {
+        ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO,
+                "Child could not open SSLMutex lockfile %s",
+                mc->szMutexFile);
+        ssl_die();
+    }
+#endif
+    return;
+}
+
+void ssl_mutex_file_remove(void *data)
+{
+#ifndef WIN32
+    SSLModConfigRec *mc = myModConfig();
+
+    /* remove the mutex lockfile */
+    unlink(mc->szMutexFile);
+#endif
+    return;
+}
+
+#ifndef WIN32
+#ifdef SSL_USE_FCNTL
+static struct flock   lock_it;
+static struct flock unlock_it;
+#endif
+#endif
+
+BOOL ssl_mutex_file_acquire(void)
+{
+    int rc = -1;
+#ifndef WIN32
+    SSLModConfigRec *mc = myModConfig();
+
+#ifdef SSL_USE_FCNTL
+    lock_it.l_whence = SEEK_SET; /* from current point */
+    lock_it.l_start  = 0;        /* -"- */
+    lock_it.l_len    = 0;        /* until end of file */
+    lock_it.l_type   = F_WRLCK;  /* set exclusive/write lock */
+    lock_it.l_pid    = 0;        /* pid not actually interesting */
+
+    while (   ((rc = fcntl(mc->nMutexFD, F_SETLKW, &lock_it)) < 0)
+           && (errno == EINTR)                                    ) 
+        ;
+#endif
+#ifdef SSL_USE_FLOCK
+    while (   ((rc = flock(mc->nMutexFD, LOCK_EX)) < 0)
+           && (errno == EINTR)                         )
+        ;
+#endif
+#endif
+
+    if (rc < 0)
+        return FALSE;
+    else
+        return TRUE;
+}
+
+BOOL ssl_mutex_file_release(void)
+{
+    int rc = -1;
+#ifndef WIN32
+    SSLModConfigRec *mc = myModConfig();
+
+#ifdef SSL_USE_FCNTL
+    unlock_it.l_whence = SEEK_SET; /* from current point */
+    unlock_it.l_start  = 0;        /* -"- */
+    unlock_it.l_len    = 0;        /* until end of file */
+    unlock_it.l_type   = F_UNLCK;  /* unlock */
+    unlock_it.l_pid    = 0;        /* pid not actually interesting */
+
+    while (   (rc = fcntl(mc->nMutexFD, F_SETLKW, &unlock_it)) < 0
+           && (errno == EINTR)                                    )
+        ;
+#endif
+#ifdef SSL_USE_FLOCK
+    while (   (rc = flock(mc->nMutexFD, LOCK_UN)) < 0
+           && (errno == EINTR)                       ) 
+        ;
+#endif
+#endif
+
+    if (rc < 0)
+        return FALSE;
+    else
+        return TRUE;
+}
+
+/*  _________________________________________________________________
+**
+**  Mutex Support (Process Semaphore)
+**  _________________________________________________________________
+*/
+
+void ssl_mutex_sem_create(server_rec *s, pool *p)
+{
+#ifdef SSL_CAN_USE_SEM
+    int semid;
+    SSLModConfigRec *mc = myModConfig();
+#ifdef SSL_HAVE_IPCSEM
+    union ssl_ipc_semun semctlarg;
+    struct semid_ds semctlbuf;
+#endif
+
+#ifdef SSL_HAVE_IPCSEM
+    semid = semget(IPC_PRIVATE, 1, IPC_CREAT|IPC_EXCL|S_IRUSR|S_IWUSR);
+    if (semid == -1 && errno == EEXIST)
+        semid = semget(IPC_PRIVATE, 1, IPC_EXCL|S_IRUSR|S_IWUSR);
+    if (semid == -1) {
+        ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO,
+                "Parent process could not create private SSLMutex semaphore");
+        ssl_die();
+    }
+    semctlarg.val = 0;
+    if (semctl(semid, 0, SETVAL, semctlarg) < 0) {
+        ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO,
+                "Parent process could not initialize SSLMutex semaphore value");
+        ssl_die();
+    }
+    semctlbuf.sem_perm.uid  = ap_user_id;
+    semctlbuf.sem_perm.gid  = ap_group_id;
+    semctlbuf.sem_perm.mode = 0660;
+    semctlarg.buf = &semctlbuf;
+    if (semctl(semid, 0, IPC_SET, semctlarg) < 0) {
+        ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO,
+                "Parent process could not set permissions for SSLMutex semaphore");
+        ssl_die();
+    }
+#endif
+#ifdef SSL_HAVE_W32SEM
+    semid = (int)ap_create_mutex("mod_ssl_mutex");
+#endif
+    mc->nMutexSEMID = semid;
+#endif
+    return;
+}
+
+void ssl_mutex_sem_open(server_rec *s, pool *p)
+{
+#ifdef SSL_CAN_USE_SEM
+#ifdef SSL_HAVE_W32SEM
+    SSLModConfigRec *mc = myModConfig();
+
+    mc->nMutexSEMID = (int)ap_open_mutex("mod_ssl_mutex");
+#endif
+#endif
+    return;
+}
+
+void ssl_mutex_sem_remove(void *data)
+{
+#ifdef SSL_CAN_USE_SEM
+    SSLModConfigRec *mc = myModConfig();
+
+#ifdef SSL_HAVE_IPCSEM
+    semctl(mc->nMutexSEMID, 0, IPC_RMID, 0);
+#endif
+#ifdef SSL_HAVE_W32SEM
+    ap_destroy_mutex((mutex *)mc->nMutexSEMID);
+#endif
+#endif
+    return;
+}
+
+BOOL ssl_mutex_sem_acquire(void)
+{
+    int rc = 0;
+#ifdef SSL_CAN_USE_SEM
+    SSLModConfigRec *mc = myModConfig();
+
+#ifdef SSL_HAVE_IPCSEM
+    struct sembuf sb[] = {
+        { 0, 0, 0 },       /* wait for semaphore */
+        { 0, 1, SEM_UNDO } /* increment semaphore */
+    };
+
+    while (   (rc = semop(mc->nMutexSEMID, sb, 2)) < 0
+           && (errno == EINTR)                        ) 
+        ;
+#endif
+#ifdef SSL_HAVE_W32SEM
+    rc = ap_acquire_mutex((mutex *)mc->nMutexSEMID);
+#endif
+#endif
+    if (rc != 0)
+        return FALSE;
+    else
+        return TRUE;
+}
+
+BOOL ssl_mutex_sem_release(void)
+{
+    int rc = 0;
+#ifdef SSL_CAN_USE_SEM
+    SSLModConfigRec *mc = myModConfig();
+
+#ifdef SSL_HAVE_IPCSEM
+    struct sembuf sb[] = {
+        { 0, -1, SEM_UNDO } /* decrements semaphore */
+    };
+
+    while (   (rc = semop(mc->nMutexSEMID, sb, 1)) < 0 
+           && (errno == EINTR)                        ) 
+        ;
+#endif
+#ifdef SSL_HAVE_W32SEM
+    rc = ap_release_mutex((mutex *)mc->nMutexSEMID);
+#endif
+#endif
+    if (rc != 0)
+        return FALSE;
+    else
+        return TRUE;
+}
+
diff --git a/modules/ssl/ssl_engine_pphrase.c b/modules/ssl/ssl_engine_pphrase.c
new file mode 100644 (file)
index 0000000..2cef4e3
--- /dev/null
@@ -0,0 +1,545 @@
+/*                      _             _
+**  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
+** | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
+** | | | | | | (_) | (_| |   \__ \__ \ |  www.modssl.org
+** |_| |_| |_|\___/ \__,_|___|___/___/_|  ftp.modssl.org
+**                      |_____|
+**  ssl_engine_pphrase.c
+**  Pass Phrase Dialog
+*/
+
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ *    nor may "mod_ssl" appear in their names without prior
+ *    written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+                             /* ``Treat your password like your
+                                  toothbrush. Don't let anybody
+                                  else use it, and get a new one
+                                  every six months.''
+                                           -- Clifford Stoll     */
+#include "mod_ssl.h"
+
+
+/*  _________________________________________________________________
+**
+**  Pass Phrase and Private Key Handling
+**  _________________________________________________________________
+*/
+
+#define STDERR_FILENO_STORE 50
+#define BUILTIN_DIALOG_BACKOFF 2
+#define BUILTIN_DIALOG_RETRIES 5
+
+void ssl_pphrase_Handle(server_rec *s, pool *p)
+{
+    SSLModConfigRec *mc = myModConfig();
+    SSLSrvConfigRec *sc;
+    server_rec *pServ;
+    char *cpVHostID;
+    char szPath[MAX_STRING_LEN];
+    EVP_PKEY *pPrivateKey;
+    ssl_asn1_t *asn1;
+    unsigned char *ucp;
+    X509 *pX509Cert;
+    FILE *fp;
+    BOOL bReadable;
+    ssl_ds_array *aPassPhrase;
+    int nPassPhrase;
+    int nPassPhraseCur;
+    char *cpPassPhraseCur;
+    int nPassPhraseRetry;
+    int nPassPhraseDialog;
+    int nPassPhraseDialogCur;
+    BOOL bPassPhraseDialogOnce;
+    char **cpp;
+    int i, j;
+    ssl_algo_t algoCert, algoKey, at;
+    char *an;
+    char *cp;
+
+    /*
+     * Start with a fresh pass phrase array
+     */
+    aPassPhrase       = ssl_ds_array_make(p, sizeof(char *));
+    nPassPhrase       = 0;
+    nPassPhraseDialog = 0;
+
+    /*
+     * Walk through all configured servers
+     */
+    for (pServ = s; pServ != NULL; pServ = pServ->next) {
+        sc = mySrvConfig(pServ);
+
+        if (!sc->bEnabled)
+            continue;
+
+        cpVHostID = ssl_util_vhostid(p, pServ);
+        ssl_log(pServ, SSL_LOG_INFO,
+                "Init: Loading certificate & private key of SSL-aware server %s",
+                cpVHostID);
+
+        /*
+         * Read in server certificate(s): This is the easy part
+         * because this file isn't encrypted in any way.
+         */
+        if (sc->szPublicCertFile[0] == NULL) {
+            ssl_log(pServ, SSL_LOG_ERROR,
+                    "Init: Server %s should be SSL-aware but has no certificate configured "
+                    "[Hint: SSLCertificateFile]", cpVHostID);
+            ssl_die();
+        }
+        algoCert = SSL_ALGO_UNKNOWN;
+        algoKey  = SSL_ALGO_UNKNOWN;
+        for (i = 0, j = 0; i < SSL_AIDX_MAX && sc->szPublicCertFile[i] != NULL; i++) {
+
+            ap_cpystrn(szPath, sc->szPublicCertFile[i], sizeof(szPath));
+            if ((fp = ap_pfopen(p, szPath, "r")) == NULL) {
+                ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO,
+                        "Init: Can't open server certificate file %s", szPath);
+                ssl_die();
+            }
+            if ((pX509Cert = SSL_read_X509(fp, NULL, NULL)) == NULL) {
+                ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+                        "Init: Unable to read server certificate from file %s", szPath);
+                ssl_die();
+            }
+            ap_pfclose(p, fp);
+
+            /*
+             * check algorithm type of certificate and make
+             * sure only one certificate per type is used.
+             */
+            at = ssl_util_algotypeof(pX509Cert, NULL);
+            an = ssl_util_algotypestr(at);
+            if (algoCert & at) {
+                ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+                        "Init: Multiple %s server certificates not allowed", an);
+                ssl_die();
+            }
+            algoCert |= at;
+
+            /*
+             * Insert the certificate into global module configuration to let it
+             * survive the processing between the 1st Apache API init round (where
+             * we operate here) and the 2nd Apache init round (where the
+             * certificate is actually used to configure mod_ssl's per-server
+             * configuration structures).
+             */
+            cp = ap_psprintf(mc->pPool, "%s:%s", cpVHostID, an);
+            asn1 = (ssl_asn1_t *)ssl_ds_table_push(mc->tPublicCert, cp);
+            asn1->nData  = i2d_X509(pX509Cert, NULL);
+            asn1->cpData = ap_palloc(mc->pPool, asn1->nData);
+            ucp = asn1->cpData; i2d_X509(pX509Cert, &ucp); /* 2nd arg increments */
+
+            /*
+             * Free the X509 structure
+             */
+            X509_free(pX509Cert);
+
+            /*
+             * Read in the private key: This is the non-trivial part, because the
+             * key is typically encrypted, so a pass phrase dialog has to be used
+             * to request it from the user (or it has to be alternatively gathered
+             * from a dialog program). The important point here is that ISPs
+             * usually have hundrets of virtual servers configured and a lot of
+             * them use SSL, so really we have to minimize the pass phrase
+             * dialogs.
+             *
+             * The idea is this: When N virtual hosts are configured and all of
+             * them use encrypted private keys with different pass phrases, we
+             * have no chance and have to pop up N pass phrase dialogs. But
+             * usually the admin is clever enough and uses the same pass phrase
+             * for more private key files (typically he even uses one single pass
+             * phrase for all). When this is the case we can minimize the dialogs
+             * by trying to re-use already known/entered pass phrases.
+             */
+            if (sc->szPrivateKeyFile[j] != NULL)
+                ap_cpystrn(szPath, sc->szPrivateKeyFile[j++], sizeof(szPath));
+
+            /*
+             * Try to read the private key file with the help of
+             * the callback function which serves the pass
+             * phrases to OpenSSL
+             */
+            myCtxVarSet(mc,  1, pServ);
+            myCtxVarSet(mc,  2, p);
+            myCtxVarSet(mc,  3, aPassPhrase);
+            myCtxVarSet(mc,  4, &nPassPhraseCur);
+            myCtxVarSet(mc,  5, &cpPassPhraseCur);
+            myCtxVarSet(mc,  6, cpVHostID);
+            myCtxVarSet(mc,  7, an);
+            myCtxVarSet(mc,  8, &nPassPhraseDialog);
+            myCtxVarSet(mc,  9, &nPassPhraseDialogCur);
+            myCtxVarSet(mc, 10, &bPassPhraseDialogOnce);
+
+            nPassPhraseCur        = 0;
+            nPassPhraseRetry      = 0;
+            nPassPhraseDialogCur  = 0;
+            bPassPhraseDialogOnce = TRUE;
+
+                       pPrivateKey = NULL;
+
+            for (;;) {
+                /*
+                 * Try to read the private key file with the help of
+                 * the callback function which serves the pass
+                 * phrases to OpenSSL
+                 */
+                if ((fp = ap_pfopen(p, szPath, "r")) == NULL) {
+                    ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO,
+                            "Init: Can't open server private key file %s", szPath);
+                    ssl_die();
+                }
+                cpPassPhraseCur = NULL;
+                bReadable = ((pPrivateKey = SSL_read_PrivateKey(fp, NULL,
+                             ssl_pphrase_Handle_CB)) != NULL ? TRUE : FALSE);
+                ap_pfclose(p, fp);
+
+                /*
+                 * when the private key file now was readable,
+                 * it's fine and we go out of the loop
+                 */
+                if (bReadable)
+                   break;
+
+                /*
+                 * when we have more remembered pass phrases
+                 * try to reuse these first.
+                 */
+                if (nPassPhraseCur < nPassPhrase) {
+                    nPassPhraseCur++;
+                    continue;
+                }
+
+                /*
+                 * else it's not readable and we have no more
+                 * remembered pass phrases. Then this has to mean
+                 * that the callback function popped up the dialog
+                 * but a wrong pass phrase was entered.  We give the
+                 * user (but not the dialog program) a few more
+                 * chances...
+                 */
+                if (   sc->nPassPhraseDialogType == SSL_PPTYPE_BUILTIN
+                    && cpPassPhraseCur != NULL
+                    && nPassPhraseRetry < BUILTIN_DIALOG_RETRIES ) {
+                    fprintf(stdout, "Apache:mod_ssl:Error: Pass phrase incorrect "
+                            "(%d more retr%s permitted).\n",
+                            (BUILTIN_DIALOG_RETRIES-nPassPhraseRetry),
+                            (BUILTIN_DIALOG_RETRIES-nPassPhraseRetry) == 1 ? "y" : "ies");
+                    nPassPhraseRetry++;
+                    if (nPassPhraseRetry > BUILTIN_DIALOG_BACKOFF)
+                        sleep((nPassPhraseRetry-BUILTIN_DIALOG_BACKOFF)*5);
+                    continue;
+                }
+
+                /*
+                 * Ok, anything else now means a fatal error.
+                 */
+                if (cpPassPhraseCur == NULL)
+                    ssl_log(pServ, SSL_LOG_ERROR|SSL_ADD_SSLERR, "Init: Private key not found");
+                    if (sc->nPassPhraseDialogType == SSL_PPTYPE_BUILTIN) {
+                        fprintf(stdout, "Apache:mod_ssl:Error: Private key not found.\n");
+                        fprintf(stdout, "**Stopped\n");
+                    }
+                else {
+                    ssl_log(pServ, SSL_LOG_ERROR|SSL_ADD_SSLERR, "Init: Pass phrase incorrect");
+                    if (sc->nPassPhraseDialogType == SSL_PPTYPE_BUILTIN) {
+                        fprintf(stdout, "Apache:mod_ssl:Error: Pass phrase incorrect.\n");
+                        fprintf(stdout, "**Stopped\n");
+                    }
+                }
+                ssl_die();
+            }
+
+            if (pPrivateKey == NULL) {
+                ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+                        "Init: Unable to read server private key from file %s", szPath);
+                ssl_die();
+            }
+
+            /*
+             * check algorithm type of private key and make
+             * sure only one private key per type is used.
+             */
+            at = ssl_util_algotypeof(NULL, pPrivateKey);
+            an = ssl_util_algotypestr(at);
+            if (algoKey & at) {
+                ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+                        "Init: Multiple %s server private keys not allowed", an);
+                ssl_die();
+            }
+            algoKey |= at;
+
+            /*
+             * Log the type of reading
+             */
+            if (nPassPhraseDialogCur == 0)
+                ssl_log(pServ, SSL_LOG_TRACE, 
+                        "Init: (%s) unencrypted %s private key - pass phrase not required", 
+                        cpVHostID, an);
+            else {
+                if (cpPassPhraseCur != NULL)
+                    ssl_log(pServ, SSL_LOG_TRACE, 
+                            "Init: (%s) encrypted %s private key - pass phrase requested", 
+                            cpVHostID, an);
+                else
+                    ssl_log(pServ, SSL_LOG_TRACE, 
+                            "Init: (%s) encrypted %s private key - pass phrase reused", 
+                            cpVHostID, an);
+            }
+
+            /*
+             * Ok, when we have one more pass phrase store it
+             */
+            if (cpPassPhraseCur != NULL) {
+                cpp = (char **)ssl_ds_array_push(aPassPhrase);
+                *cpp = cpPassPhraseCur;
+                nPassPhrase++;
+            }
+
+            /*
+             * Insert private key into the global module configuration
+             * (we convert it to a stand-alone DER byte sequence
+             * because the SSL library uses static variables inside a
+             * RSA structure which do not survive DSO reloads!)
+             */
+            cp = ap_psprintf(mc->pPool, "%s:%s", cpVHostID, an);
+            asn1 = (ssl_asn1_t *)ssl_ds_table_push(mc->tPrivateKey, cp);
+            asn1->nData  = i2d_PrivateKey(pPrivateKey, NULL);
+            asn1->cpData = ap_palloc(mc->pPool, asn1->nData);
+            ucp = asn1->cpData; i2d_PrivateKey(pPrivateKey, &ucp); /* 2nd arg increments */
+
+            /*
+             * Free the private key structure
+             */
+            EVP_PKEY_free(pPrivateKey);
+        }
+    }
+
+    /*
+     * Let the user know when we're successful.
+     */
+    if (nPassPhraseDialog > 0) {
+        sc = mySrvConfig(s);
+        if (sc->nPassPhraseDialogType == SSL_PPTYPE_BUILTIN) {
+            fprintf(stdout, "\n");
+            fprintf(stdout, "Ok: Pass Phrase Dialog successful.\n");
+        }
+    }
+
+    /*
+     * Wipe out the used memory from the
+     * pass phrase array and then deallocate it
+     */
+    if (!ssl_ds_array_isempty(aPassPhrase)) {
+        ssl_ds_array_wipeout(aPassPhrase);
+        ssl_ds_array_kill(aPassPhrase);
+        ssl_log(s, SSL_LOG_INFO, "Init: Wiped out the queried pass phrases from memory");
+    }
+
+    return;
+}
+
+int ssl_pphrase_Handle_CB(char *buf, int bufsize, int verify)
+{
+    SSLModConfigRec *mc = myModConfig();
+    server_rec *s;
+    pool *p;
+    ssl_ds_array *aPassPhrase;
+    SSLSrvConfigRec *sc;
+    int *pnPassPhraseCur;
+    char **cppPassPhraseCur;
+    char *cpVHostID;
+    char *cpAlgoType;
+    int *pnPassPhraseDialog;
+    int *pnPassPhraseDialogCur;
+    BOOL *pbPassPhraseDialogOnce;
+    int stderr_store;
+    char **cpp;
+    int len = -1;
+
+    /*
+     * Reconnect to the context of ssl_phrase_Handle()
+     */
+    s                      = myCtxVarGet(mc,  1, server_rec *);
+    p                      = myCtxVarGet(mc,  2, pool *);
+    aPassPhrase            = myCtxVarGet(mc,  3, ssl_ds_array *);
+    pnPassPhraseCur        = myCtxVarGet(mc,  4, int *);
+    cppPassPhraseCur       = myCtxVarGet(mc,  5, char **);
+    cpVHostID              = myCtxVarGet(mc,  6, char *);
+    cpAlgoType             = myCtxVarGet(mc,  7, char *);
+    pnPassPhraseDialog     = myCtxVarGet(mc,  8, int *);
+    pnPassPhraseDialogCur  = myCtxVarGet(mc,  9, int *);
+    pbPassPhraseDialogOnce = myCtxVarGet(mc, 10, BOOL *);
+    sc                     = mySrvConfig(s);
+
+    (*pnPassPhraseDialog)++;
+    (*pnPassPhraseDialogCur)++;
+
+    /*
+     * When remembered pass phrases are available use them...
+     */
+    if ((cpp = (char **)ssl_ds_array_get(aPassPhrase, *pnPassPhraseCur)) != NULL) {
+        ap_cpystrn(buf, *cpp, bufsize);
+        len = strlen(buf);
+        return len;
+    }
+
+    /*
+     * Builtin dialog
+     */
+    if (sc->nPassPhraseDialogType == SSL_PPTYPE_BUILTIN) {
+        char *prompt;
+        int i;
+#ifdef WIN32
+        FILE *con;
+#endif
+
+        ssl_log(s, SSL_LOG_INFO,
+                "Init: Requesting pass phrase via builtin terminal dialog");
+
+        /*
+         * Reconnect STDERR to terminal (here STDOUT) because
+         * at our init stage Apache already connected STDERR
+         * to the general error logfile.
+         */
+#ifdef WIN32
+        stderr_store = STDERR_FILENO_STORE;
+#else
+        if ((stderr_store = open("/dev/null", O_WRONLY)) == -1)
+            stderr_store = STDERR_FILENO_STORE;
+#endif
+        dup2(STDERR_FILENO, stderr_store);
+#ifdef WIN32
+        if ((con = fopen("con", "w")) != NULL)
+            dup2(fileno(con), STDERR_FILENO);
+        else
+            dup2(STDOUT_FILENO, STDERR_FILENO);
+#else
+        dup2(STDOUT_FILENO, STDERR_FILENO);
+#endif
+
+        /*
+         * The first time display a header to inform the user about what
+         * program he actually speaks to, which module is responsible for
+         * this terminal dialog and why to the hell he has to enter
+         * something...
+         */
+        if (*pnPassPhraseDialog == 1) {
+            fprintf(stderr, "%s mod_ssl/%s (Pass Phrase Dialog)\n",
+                    SERVER_BASEVERSION, MOD_SSL_VERSION);
+            fprintf(stderr, "Some of your private key files are encrypted for security reasons.\n");
+            fprintf(stderr, "In order to read them you have to provide us with the pass phrases.\n");
+        }
+        if (*pbPassPhraseDialogOnce) {
+            *pbPassPhraseDialogOnce = FALSE;
+            fprintf(stderr, "\n");
+            fprintf(stderr, "Server %s (%s)\n", cpVHostID, cpAlgoType);
+        }
+
+        /*
+         * Emulate the OpenSSL internal pass phrase dialog
+         * (see crypto/pem/pem_lib.c:def_callback() for details)
+         */
+        prompt = "Enter pass phrase:";
+        for (;;) {
+            if ((i = EVP_read_pw_string(buf, bufsize, prompt, FALSE)) != 0) {
+                PEMerr(PEM_F_DEF_CALLBACK,PEM_R_PROBLEMS_GETTING_PASSWORD);
+                memset(buf, 0, (unsigned int)bufsize);
+                return (-1);
+            }
+            len = strlen(buf);
+            if (len < 1)
+                fprintf(stderr, "Apache:mod_ssl:Error: Pass phrase empty (needs to be at least 1 character).\n");
+            else
+                break;
+        }
+
+        /*
+         * Restore STDERR to Apache error logfile
+         */
+        dup2(stderr_store, STDERR_FILENO);
+        close(stderr_store);
+#ifdef WIN32
+        if (con != NULL)
+            fclose(con);
+#endif
+    }
+
+    /*
+     * Filter program
+     */
+    else if (sc->nPassPhraseDialogType == SSL_PPTYPE_FILTER) {
+        char *cmd;
+        char *result;
+
+        ssl_log(s, SSL_LOG_INFO,
+                "Init: Requesting pass phrase from dialog filter program (%s)",
+                sc->szPassPhraseDialogPath);
+
+        if (strchr(sc->szPassPhraseDialogPath, ' ') != NULL)
+            cmd = ap_psprintf(p, "\"%s\" %s %s", sc->szPassPhraseDialogPath, cpVHostID, cpAlgoType);
+        else
+            cmd = ap_psprintf(p, "%s %s %s", sc->szPassPhraseDialogPath, cpVHostID, cpAlgoType);
+        result = ssl_util_readfilter(s, p, cmd);
+        ap_cpystrn(buf, result, bufsize);
+        len = strlen(buf);
+    }
+
+    /*
+     * Ok, we now have the pass phrase, so give it back
+     */
+    *cppPassPhraseCur = ap_pstrdup(p, buf);
+
+    /*
+     * And return it's length to OpenSSL...
+     */
+    return (len);
+}
+
diff --git a/modules/ssl/ssl_engine_rand.c b/modules/ssl/ssl_engine_rand.c
new file mode 100644 (file)
index 0000000..afb49b4
--- /dev/null
@@ -0,0 +1,215 @@
+/*                      _             _
+**  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
+** | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
+** | | | | | | (_) | (_| |   \__ \__ \ |  www.modssl.org
+** |_| |_| |_|\___/ \__,_|___|___/___/_|  ftp.modssl.org
+**                      |_____|
+**  ssl_engine_rand.c
+**  Random Number Generator Seeding
+*/
+
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ *    nor may "mod_ssl" appear in their names without prior
+ *    written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+                             /* ``The generation of random
+                                  numbers is too important
+                                  to be left to chance.'' */
+
+#include "mod_ssl.h"
+
+
+/*  _________________________________________________________________
+**
+**  Support for better seeding of SSL library's RNG
+**  _________________________________________________________________
+*/
+
+static int ssl_rand_choosenum(int, int);
+static int ssl_rand_feedfp(pool *, FILE *, int);
+
+int ssl_rand_seed(server_rec *s, pool *p, ssl_rsctx_t nCtx, char *prefix)
+{
+    SSLModConfigRec *mc;
+    array_header *apRandSeed;
+    ssl_randseed_t *pRandSeeds;
+    ssl_randseed_t *pRandSeed;
+    unsigned char stackdata[256];
+    int nReq, nDone;
+    FILE *fp;
+    int i, n, l;
+    time_t t;
+    pid_t pid;
+
+    mc = myModConfig();
+    nReq  = 0;
+    nDone = 0;
+    apRandSeed = mc->aRandSeed;
+    pRandSeeds = (ssl_randseed_t *)apRandSeed->elts;
+    for (i = 0; i < apRandSeed->nelts; i++) {
+        pRandSeed = &pRandSeeds[i];
+        if (pRandSeed->nCtx == nCtx) {
+            nReq += pRandSeed->nBytes;
+            if (pRandSeed->nSrc == SSL_RSSRC_FILE) {
+                /*
+                 * seed in contents of an external file
+                 */
+                if ((fp = ap_pfopen(p, pRandSeed->cpPath, "r")) == NULL)
+                    continue;
+                nDone += ssl_rand_feedfp(p, fp, pRandSeed->nBytes);
+                ap_pfclose(p, fp);
+            }
+            else if (pRandSeed->nSrc == SSL_RSSRC_EXEC) {
+                /*
+                 * seed in contents generated by an external program
+                 */
+                if ((fp = ssl_util_ppopen(s, p, ap_psprintf(p, "%s %d",
+                                          pRandSeed->cpPath, pRandSeed->nBytes))) == NULL)
+                    continue;
+                nDone += ssl_rand_feedfp(p, fp, pRandSeed->nBytes);
+                ssl_util_ppclose(s, p, fp);
+            }
+#if SSL_LIBRARY_VERSION >= 0x00905100
+            else if (pRandSeed->nSrc == SSL_RSSRC_EGD) {
+                /*
+                 * seed in contents provided by the external
+                 * Entropy Gathering Daemon (EGD)
+                 */
+                if ((n = RAND_egd(pRandSeed->cpPath)) == -1)
+                    continue;
+                nDone += n;
+            }
+#endif
+            else if (pRandSeed->nSrc == SSL_RSSRC_BUILTIN) {
+                /*
+                 * seed in the current time (usually just 4 bytes)
+                 */
+                t = time(NULL);
+                l = sizeof(time_t);
+                RAND_seed((unsigned char *)&t, l);
+                nDone += l;
+
+                /*
+                 * seed in the current process id (usually just 4 bytes)
+                 */
+                pid = getpid();
+                l = sizeof(pid_t);
+                RAND_seed((unsigned char *)&pid, l);
+                nDone += l;
+                
+                /*
+                 * seed in some current state of the run-time stack (128 bytes)
+                 */
+                n = ssl_rand_choosenum(0, sizeof(stackdata)-128-1);
+                RAND_seed(stackdata+n, 128);
+                nDone += 128;
+
+                /*
+                 * seed in an 1KB extract of the current scoreboard
+                 */
+                if (ap_scoreboard_image != NULL) {
+                    n = ssl_rand_choosenum(0, SCOREBOARD_SIZE-1024-1);
+                    RAND_seed((unsigned char *)ap_scoreboard_image+n, 1024);
+                    nDone += 1024;
+                }
+            }
+        }
+    }
+    ssl_log(s, SSL_LOG_INFO, "%sSeeding PRNG with %d bytes of entropy", prefix, nDone);
+
+#if SSL_LIBRARY_VERSION >= 0x00905100
+    if (RAND_status() == 0)
+        ssl_log(s, SSL_LOG_WARN, "%sPRNG still contains not sufficient entropy!", prefix);
+#endif
+    return nDone;
+}
+
+#define BUFSIZE 8192
+
+static int ssl_rand_feedfp(pool *p, FILE *fp, int nReq)
+{
+    int nDone;
+    unsigned char caBuf[BUFSIZE];
+    int nBuf;
+    int nRead;
+    int nTodo;
+
+    nDone = 0;
+    nRead = BUFSIZE;
+    nTodo = nReq;
+    while (1) {
+        if (nReq > 0)
+            nRead = (nTodo < BUFSIZE ? nTodo : BUFSIZE);
+        if ((nBuf = (int)fread(caBuf, 1, nRead, fp)) <= 0)
+            break;
+        RAND_seed(caBuf, nBuf);
+        nDone += nBuf;
+        if (nReq > 0) {
+            nTodo -= nBuf;
+            if (nTodo <= 0)
+                break;
+        }
+    }
+    return nDone;
+}
+
+static int ssl_rand_choosenum(int l, int h)
+{
+    int i;
+    char buf[50];
+
+    srand((unsigned int)time(NULL));
+    ap_snprintf(buf, sizeof(buf), "%.0f",
+                (((double)(rand()%RAND_MAX)/RAND_MAX)*(h-l)));
+    i = atoi(buf)+1;
+    if (i < l) i = l;
+    if (i > h) i = h;
+    return i;
+}
+
diff --git a/modules/ssl/ssl_engine_vars.c b/modules/ssl/ssl_engine_vars.c
new file mode 100644 (file)
index 0000000..c0755a5
--- /dev/null
@@ -0,0 +1,615 @@
+/*                      _             _
+**  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
+** | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
+** | | | | | | (_) | (_| |   \__ \__ \ |  www.modssl.org
+** |_| |_| |_|\___/ \__,_|___|___/___/_|  ftp.modssl.org
+**                      |_____|
+**  ssl_engine_vars.c
+**  Variable Lookup Facility
+*/
+
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ *    nor may "mod_ssl" appear in their names without prior
+ *    written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+                             /* ``Those of you who think they
+                                  know everything are very annoying
+                                  to those of us who do.''
+                                                  -- Unknown       */
+#include "mod_ssl.h"
+
+
+/*  _________________________________________________________________
+**
+**  Variable Lookup
+**  _________________________________________________________________
+*/
+
+static char *ssl_var_lookup_header(pool *p, request_rec *r, const char *name);
+static char *ssl_var_lookup_ssl(pool *p, conn_rec *c, char *var);
+static char *ssl_var_lookup_ssl_cert(pool *p, X509 *xs, char *var);
+static char *ssl_var_lookup_ssl_cert_dn(pool *p, X509_NAME *xsname, char *var);
+static char *ssl_var_lookup_ssl_cert_valid(pool *p, ASN1_UTCTIME *tm);
+static char *ssl_var_lookup_ssl_cert_serial(pool *p, X509 *xs);
+static char *ssl_var_lookup_ssl_cert_chain(pool *p, STACK_OF(X509) *sk, char *var);
+static char *ssl_var_lookup_ssl_cert_PEM(pool *p, X509 *xs);
+static char *ssl_var_lookup_ssl_cert_verify(pool *p, conn_rec *c);
+static char *ssl_var_lookup_ssl_cipher(pool *p, conn_rec *c, char *var);
+static void  ssl_var_lookup_ssl_cipher_bits(SSL *ssl, int *usekeysize, int *algkeysize);
+static char *ssl_var_lookup_ssl_version(pool *p, char *var);
+
+void ssl_var_register(void)
+{
+    ap_hook_configure("ap::mod_ssl::var_lookup",
+                      AP_HOOK_SIG6(ptr,ptr,ptr,ptr,ptr,ptr), AP_HOOK_DECLINE(NULL));
+    ap_hook_register("ap::mod_ssl::var_lookup",
+                     ssl_var_lookup, AP_HOOK_NOCTX);
+    return;
+}
+
+void ssl_var_unregister(void)
+{
+    ap_hook_unregister("ap::mod_ssl::var_lookup", ssl_var_lookup);
+    return;
+}
+
+char *ssl_var_lookup(pool *p, server_rec *s, conn_rec *c, request_rec *r, char *var)
+{
+    SSLModConfigRec *mc = myModConfig();
+    char *result;
+    BOOL resdup;
+    time_t tc;
+    struct tm *tm;
+
+    result = NULL;
+    resdup = TRUE;
+
+    /*
+     * When no pool is given try to find one
+     */
+    if (p == NULL) {
+        if (r != NULL)
+            p = r->pool;
+        else if (c != NULL)
+            p = c->pool;
+        else
+            p = mc->pPool;
+    }
+
+    /*
+     * Request dependent stuff
+     */
+    if (r != NULL) {
+        if (strcEQ(var, "HTTP_USER_AGENT"))
+            result = ssl_var_lookup_header(p, r, "User-Agent");
+        else if (strcEQ(var, "HTTP_REFERER"))
+            result = ssl_var_lookup_header(p, r, "Referer");
+        else if (strcEQ(var, "HTTP_COOKIE"))
+            result = ssl_var_lookup_header(p, r, "Cookie");
+        else if (strcEQ(var, "HTTP_FORWARDED"))
+            result = ssl_var_lookup_header(p, r, "Forwarded");
+        else if (strcEQ(var, "HTTP_HOST"))
+            result = ssl_var_lookup_header(p, r, "Host");
+        else if (strcEQ(var, "HTTP_PROXY_CONNECTION"))
+            result = ssl_var_lookup_header(p, r, "Proxy-Connection");
+        else if (strcEQ(var, "HTTP_ACCEPT"))
+            result = ssl_var_lookup_header(p, r, "Accept");
+        else if (strlen(var) > 5 && strcEQn(var, "HTTP:", 5))
+            /* all other headers from which we are still not know about */
+            result = ssl_var_lookup_header(p, r, var+5);
+        else if (strcEQ(var, "THE_REQUEST"))
+            result = r->the_request;
+        else if (strcEQ(var, "REQUEST_METHOD"))
+            result = (char *)(r->method);
+        else if (strcEQ(var, "REQUEST_SCHEME"))
+            result = ap_http_method(r);
+        else if (strcEQ(var, "REQUEST_URI"))
+            result = r->uri;
+        else if (strcEQ(var, "SCRIPT_FILENAME") ||
+                 strcEQ(var, "REQUEST_FILENAME"))
+            result = r->filename;
+        else if (strcEQ(var, "PATH_INFO"))
+            result = r->path_info;
+        else if (strcEQ(var, "QUERY_STRING"))
+            result = r->args;
+        else if (strcEQ(var, "REMOTE_HOST"))
+            result = (char *)ap_get_remote_host(r->connection,
+                                                r->per_dir_config, REMOTE_NAME);
+        else if (strcEQ(var, "REMOTE_IDENT"))
+            result = (char *)ap_get_remote_logname(r);
+        else if (strcEQ(var, "IS_SUBREQ"))
+            result = (r->main != NULL ? "true" : "false");
+        else if (strcEQ(var, "DOCUMENT_ROOT"))
+            result = (char *)ap_document_root(r);
+        else if (strcEQ(var, "SERVER_ADMIN"))
+            result = r->server->server_admin;
+        else if (strcEQ(var, "SERVER_NAME"))
+            result = (char *)ap_get_server_name(r);
+        else if (strcEQ(var, "SERVER_PORT"))
+            result = ap_psprintf(p, "%u", ap_get_server_port(r));
+        else if (strcEQ(var, "SERVER_PROTOCOL"))
+            result = r->protocol;
+    }
+
+    /*
+     * Connection stuff
+     */
+    if (result == NULL && c != NULL) {
+        if (strcEQ(var, "REMOTE_ADDR"))
+            result = c->remote_ip;
+        else if (strcEQ(var, "REMOTE_USER"))
+            result = c->user;
+        else if (strcEQ(var, "AUTH_TYPE"))
+            result = c->ap_auth_type;
+        else if (strlen(var) > 4 && strcEQn(var, "SSL_", 4))
+            result = ssl_var_lookup_ssl(p, c, var+4);
+        else if (strcEQ(var, "HTTPS")) {
+            if (ap_ctx_get(c->client->ctx, "ssl") != NULL)
+                result = "on";
+            else
+                result = "off";
+        }
+    }
+
+    /*
+     * Totally independent stuff
+     */
+    if (result == NULL) {
+        if (strlen(var) > 12 && strcEQn(var, "SSL_VERSION_", 12))
+            result = ssl_var_lookup_ssl_version(p, var+12);
+        else if (strcEQ(var, "SERVER_SOFTWARE"))
+            result = (char *)ap_get_server_version();
+        else if (strcEQ(var, "API_VERSION")) {
+            result = ap_psprintf(p, "%d", MODULE_MAGIC_NUMBER);
+            resdup = FALSE;
+        }
+        else if (strcEQ(var, "TIME_YEAR")) {
+            tc = time(NULL);
+            tm = localtime(&tc);
+            result = ap_psprintf(p, "%02d%02d",
+                                 (tm->tm_year / 100) + 19, tm->tm_year % 100);
+            resdup = FALSE;
+        }
+#define MKTIMESTR(format, tmfield) \
+            tc = time(NULL); \
+            tm = localtime(&tc); \
+            result = ap_psprintf(p, format, tm->tmfield); \
+            resdup = FALSE;
+        else if (strcEQ(var, "TIME_MON")) {
+            MKTIMESTR("%02d", tm_mon+1)
+        }
+        else if (strcEQ(var, "TIME_DAY")) {
+            MKTIMESTR("%02d", tm_mday)
+        }
+        else if (strcEQ(var, "TIME_HOUR")) {
+            MKTIMESTR("%02d", tm_hour)
+        }
+        else if (strcEQ(var, "TIME_MIN")) {
+            MKTIMESTR("%02d", tm_min)
+        }
+        else if (strcEQ(var, "TIME_SEC")) {
+            MKTIMESTR("%02d", tm_sec)
+        }
+        else if (strcEQ(var, "TIME_WDAY")) {
+            MKTIMESTR("%d", tm_wday)
+        }
+        else if (strcEQ(var, "TIME")) {
+            tc = time(NULL);
+            tm = localtime(&tc);
+            result = ap_psprintf(p,
+                        "%02d%02d%02d%02d%02d%02d%02d", (tm->tm_year / 100) + 19,
+                        (tm->tm_year % 100), tm->tm_mon+1, tm->tm_mday,
+                        tm->tm_hour, tm->tm_min, tm->tm_sec);
+            resdup = FALSE;
+        }
+        /* all other env-variables from the parent Apache process */
+        else if (strlen(var) > 4 && strcEQn(var, "ENV:", 4)) {
+            result = (char *)ap_table_get(r->notes, var+4);
+            if (result == NULL)
+                result = (char *)ap_table_get(r->subprocess_env, var+4);
+            if (result == NULL)
+                result = getenv(var+4);
+        }
+    }
+
+    if (result != NULL && resdup)
+        result = ap_pstrdup(p, result);
+    if (result == NULL)
+        result = "";
+    return result;
+}
+
+static char *ssl_var_lookup_header(pool *p, request_rec *r, const char *name)
+{
+    array_header *hdrs_arr;
+    table_entry *hdrs;
+    int i;
+
+    hdrs_arr = ap_table_elts(r->headers_in);
+    hdrs = (table_entry *)hdrs_arr->elts;
+    for (i = 0; i < hdrs_arr->nelts; ++i) {
+        if (hdrs[i].key == NULL)
+            continue;
+        if (strcEQ(hdrs[i].key, name))
+            return ap_pstrdup(p, hdrs[i].val);
+    }
+    return NULL;
+}
+
+static char *ssl_var_lookup_ssl(pool *p, conn_rec *c, char *var)
+{
+    char *result;
+    X509 *xs;
+    STACK_OF(X509) *sk;
+    SSL *ssl;
+
+    result = NULL;
+
+    ssl = ap_ctx_get(c->client->ctx, "ssl");
+    if (strlen(var) > 8 && strcEQn(var, "VERSION_", 8)) {
+        result = ssl_var_lookup_ssl_version(p, var+8);
+    }
+    else if (ssl != NULL && strcEQ(var, "PROTOCOL")) {
+        result = (char *)SSL_get_version(ssl);
+    }
+    else if (ssl != NULL && strcEQ(var, "SESSION_ID")) {
+        SSL_SESSION *pSession = SSL_get_session(ssl);
+        result = ap_pstrdup(p, SSL_SESSION_id2sz(pSession->session_id, 
+                                                 pSession->session_id_length));
+    }
+    else if (ssl != NULL && strlen(var) >= 6 && strcEQn(var, "CIPHER", 6)) {
+        result = ssl_var_lookup_ssl_cipher(p, c, var+6);
+    }
+    else if (ssl != NULL && strlen(var) > 18 && strcEQn(var, "CLIENT_CERT_CHAIN_", 18)) {
+        sk = SSL_get_peer_cert_chain(ssl);
+        result = ssl_var_lookup_ssl_cert_chain(p, sk, var+17);
+    }
+    else if (ssl != NULL && strcEQ(var, "CLIENT_VERIFY")) {
+        result = ssl_var_lookup_ssl_cert_verify(p, c);
+    }
+    else if (ssl != NULL && strlen(var) > 7 && strcEQn(var, "CLIENT_", 7)) {
+        if ((xs = SSL_get_peer_certificate(ssl)) != NULL)
+            result = ssl_var_lookup_ssl_cert(p, xs, var+7);
+    }
+    else if (ssl != NULL && strlen(var) > 7 && strcEQn(var, "SERVER_", 7)) {
+        if ((xs = SSL_get_certificate(ssl)) != NULL)
+            result = ssl_var_lookup_ssl_cert(p, xs, var+7);
+    }
+    return result;
+}
+
+static char *ssl_var_lookup_ssl_cert(pool *p, X509 *xs, char *var)
+{
+    char *result;
+    BOOL resdup;
+    X509_NAME *xsname;
+    int nid;
+    char *cp;
+
+    result = NULL;
+    resdup = TRUE;
+
+    if (strcEQ(var, "M_VERSION")) {
+        result = ap_psprintf(p, "%lu", X509_get_version(xs)+1);
+        resdup = FALSE;
+    }
+    else if (strcEQ(var, "M_SERIAL")) {
+        result = ssl_var_lookup_ssl_cert_serial(p, xs);
+    }
+    else if (strcEQ(var, "V_START")) {
+        result = ssl_var_lookup_ssl_cert_valid(p, X509_get_notBefore(xs));
+    }
+    else if (strcEQ(var, "V_END")) {
+        result = ssl_var_lookup_ssl_cert_valid(p, X509_get_notAfter(xs));
+    }
+    else if (strcEQ(var, "S_DN")) {
+        xsname = X509_get_subject_name(xs);
+        cp = X509_NAME_oneline(xsname, NULL, 0);
+        result = ap_pstrdup(p, cp);
+        free(cp);
+        resdup = FALSE;
+    }
+    else if (strlen(var) > 5 && strcEQn(var, "S_DN_", 5)) {
+        xsname = X509_get_subject_name(xs);
+        result = ssl_var_lookup_ssl_cert_dn(p, xsname, var+5);
+        resdup = FALSE;
+    }
+    else if (strcEQ(var, "I_DN")) {
+        xsname = X509_get_issuer_name(xs);
+        cp = X509_NAME_oneline(xsname, NULL, 0);
+        result = ap_pstrdup(p, cp);
+        free(cp);
+        resdup = FALSE;
+    }
+    else if (strlen(var) > 5 && strcEQn(var, "I_DN_", 5)) {
+        xsname = X509_get_issuer_name(xs);
+        result = ssl_var_lookup_ssl_cert_dn(p, xsname, var+5);
+        resdup = FALSE;
+    }
+    else if (strcEQ(var, "A_SIG")) {
+        nid = OBJ_obj2nid(xs->cert_info->signature->algorithm);
+        result = ap_pstrdup(p, (nid == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(nid));
+        resdup = FALSE;
+    }
+    else if (strcEQ(var, "A_KEY")) {
+        nid = OBJ_obj2nid(xs->cert_info->key->algor->algorithm);
+        result = ap_pstrdup(p, (nid == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(nid));
+        resdup = FALSE;
+    }
+    else if (strcEQ(var, "CERT")) {
+        result = ssl_var_lookup_ssl_cert_PEM(p, xs);
+    }
+
+    if (result != NULL && resdup)
+        result = ap_pstrdup(p, result);
+    return result;
+}
+
+static const struct {
+    char *name;
+    int   nid;
+} ssl_var_lookup_ssl_cert_dn_rec[] = {
+    { "C",     NID_countryName            },
+    { "ST",    NID_stateOrProvinceName    }, /* officially    (RFC2156) */
+    { "SP",    NID_stateOrProvinceName    }, /* compatibility (SSLeay)  */
+    { "L",     NID_localityName           },
+    { "O",     NID_organizationName       },
+    { "OU",    NID_organizationalUnitName },
+    { "CN",    NID_commonName             },
+    { "T",     NID_title                  },
+    { "I",     NID_initials               },
+    { "G",     NID_givenName              },
+    { "S",     NID_surname                },
+    { "D",     NID_description            },
+    { "UID",   NID_uniqueIdentifier       },
+    { "Email", NID_pkcs9_emailAddress     },
+    { NULL,    0                          }
+};
+
+static char *ssl_var_lookup_ssl_cert_dn(pool *p, X509_NAME *xsname, char *var)
+{
+    char *result;
+    X509_NAME_ENTRY *xsne;
+    int i, j, n;
+
+    result = NULL;
+
+    for (i = 0; ssl_var_lookup_ssl_cert_dn_rec[i].name != NULL; i++) {
+        if (strEQ(var, ssl_var_lookup_ssl_cert_dn_rec[i].name)) {
+            for (j = 0; j < sk_X509_NAME_ENTRY_num(xsname->entries); j++) {
+                xsne = sk_X509_NAME_ENTRY_value(xsname->entries, j);
+                n = OBJ_obj2nid(xsne->object);
+                if (n == ssl_var_lookup_ssl_cert_dn_rec[i].nid) {
+                    result = ap_palloc(p, xsne->value->length+1);
+                    ap_cpystrn(result, (char *)xsne->value->data, xsne->value->length+1);
+#ifdef CHARSET_EBCDIC
+                    ascii2ebcdic(result, result, xsne->value->length);
+#endif /* CHARSET_EBCDIC */
+                    result[xsne->value->length] = NUL;
+                    break;
+                }
+            }
+            break;
+        }
+    }
+    return result;
+}
+
+static char *ssl_var_lookup_ssl_cert_valid(pool *p, ASN1_UTCTIME *tm)
+{
+    char *result;
+    BIO* bio;
+    int n;
+
+    if ((bio = BIO_new(BIO_s_mem())) == NULL)
+        return NULL;
+    ASN1_UTCTIME_print(bio, tm);
+    n = BIO_pending(bio);
+    result = ap_pcalloc(p, n+1);
+    n = BIO_read(bio, result, n);
+    result[n] = NUL;
+    BIO_free(bio);
+    return result;
+}
+
+static char *ssl_var_lookup_ssl_cert_serial(pool *p, X509 *xs)
+{
+    char *result;
+    BIO *bio;
+    int n;
+
+    if ((bio = BIO_new(BIO_s_mem())) == NULL)
+        return NULL;
+    i2a_ASN1_INTEGER(bio, X509_get_serialNumber(xs));
+    n = BIO_pending(bio);
+    result = ap_pcalloc(p, n+1);
+    n = BIO_read(bio, result, n);
+    result[n] = NUL;
+    BIO_free(bio);
+    return result;
+}
+
+static char *ssl_var_lookup_ssl_cert_chain(pool *p, STACK_OF(X509) *sk, char *var)
+{
+    char *result;
+    X509 *xs;
+    int n;
+
+    result = NULL;
+
+    if (strspn(var, "0123456789") == strlen(var)) {
+        n = atoi(var);
+        if (n < sk_X509_num(sk)) {
+            xs = sk_X509_value(sk, n);
+            result = ssl_var_lookup_ssl_cert_PEM(p, xs);
+        }
+    }
+
+    return result;
+}
+
+static char *ssl_var_lookup_ssl_cert_PEM(pool *p, X509 *xs)
+{
+    char *result;
+    BIO *bio;
+    int n;
+
+    if ((bio = BIO_new(BIO_s_mem())) == NULL)
+        return NULL;
+    PEM_write_bio_X509(bio, xs);
+    n = BIO_pending(bio);
+    result = ap_pcalloc(p, n+1);
+    n = BIO_read(bio, result, n);
+    result[n] = NUL;
+    BIO_free(bio);
+    return result;
+}
+
+static char *ssl_var_lookup_ssl_cert_verify(pool *p, conn_rec *c)
+{
+    char *result;
+    long vrc;
+    char *verr;
+    char *vinfo;
+    SSL *ssl;
+    X509 *xs;
+
+    result = NULL;
+    ssl   = ap_ctx_get(c->client->ctx, "ssl");
+    verr  = ap_ctx_get(c->client->ctx, "ssl::verify::error");
+    vinfo = ap_ctx_get(c->client->ctx, "ssl::verify::info");
+    vrc   = SSL_get_verify_result(ssl);
+    xs    = SSL_get_peer_certificate(ssl);
+
+    if (vrc == X509_V_OK && verr == NULL && vinfo == NULL && xs == NULL)
+        /* no client verification done at all */
+        result = "NONE";
+    else if (vrc == X509_V_OK && verr == NULL && vinfo == NULL && xs != NULL)
+        /* client verification done successful */
+        result = "SUCCESS";
+    else if (vrc == X509_V_OK && vinfo != NULL && strEQ(vinfo, "GENEROUS"))
+        /* client verification done in generous way */
+        result = "GENEROUS";
+    else
+        /* client verification failed */
+        result = ap_psprintf(p, "FAILED:%s", verr);
+    return result;
+}
+
+static char *ssl_var_lookup_ssl_cipher(pool *p, conn_rec *c, char *var)
+{
+    char *result;
+    BOOL resdup;
+    int usekeysize, algkeysize;
+    SSL *ssl;
+
+    result = NULL;
+    resdup = TRUE;
+
+    ssl = ap_ctx_get(c->client->ctx, "ssl");
+    ssl_var_lookup_ssl_cipher_bits(ssl, &usekeysize, &algkeysize);
+
+    if (strEQ(var, ""))
+        result = (ssl != NULL ? (char *)SSL_get_cipher_name(ssl) : NULL);
+    else if (strcEQ(var, "_EXPORT"))
+        result = (usekeysize < 56 ? "true" : "false");
+    else if (strcEQ(var, "_USEKEYSIZE")) {
+        result = ap_psprintf(p, "%d", usekeysize);
+        resdup = FALSE;
+    }
+    else if (strcEQ(var, "_ALGKEYSIZE")) {
+        result = ap_psprintf(p, "%d", algkeysize);
+        resdup = FALSE;
+    }
+
+    if (result != NULL && resdup)
+        result = ap_pstrdup(p, result);
+    return result;
+}
+
+static void ssl_var_lookup_ssl_cipher_bits(SSL *ssl, int *usekeysize, int *algkeysize)
+{
+    SSL_CIPHER *cipher;
+
+    *usekeysize = 0;
+    *algkeysize = 0;
+    if (ssl != NULL)
+        if ((cipher = SSL_get_current_cipher(ssl)) != NULL)
+            *usekeysize = SSL_CIPHER_get_bits(cipher, algkeysize);
+    return;
+}
+
+static char *ssl_var_lookup_ssl_version(pool *p, char *var)
+{
+    char *result;
+    char *cp, *cp2;
+
+    result = NULL;
+
+    if (strEQ(var, "PRODUCT")) {
+#if defined(SSL_PRODUCT_NAME) && defined(SSL_PRODUCT_VERSION)
+        result = ap_psprintf(p, "%s/%s", SSL_PRODUCT_NAME, SSL_PRODUCT_VERSION);
+#else
+        result = NULL;
+#endif
+    }
+    else if (strEQ(var, "INTERFACE")) {
+        result = ap_psprintf(p, "mod_ssl/%s", MOD_SSL_VERSION);
+    }
+    else if (strEQ(var, "LIBRARY")) {
+        result = ap_pstrdup(p, SSL_LIBRARY_TEXT);
+        if ((cp = strchr(result, ' ')) != NULL) {
+            *cp = '/';
+            if ((cp2 = strchr(cp, ' ')) != NULL)
+                *cp2 = NUL;
+        }
+    }
+    return result;
+}
+
diff --git a/modules/ssl/ssl_expr.c b/modules/ssl/ssl_expr.c
new file mode 100644 (file)
index 0000000..49ab873
--- /dev/null
@@ -0,0 +1,119 @@
+/*                      _             _
+**  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
+** | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
+** | | | | | | (_) | (_| |   \__ \__ \ |  www.modssl.org
+** |_| |_| |_|\___/ \__,_|___|___/___/_|  ftp.modssl.org
+**                      |_____|
+**  ssl_expr.c
+**  Expression Handling
+*/
+
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ *    nor may "mod_ssl" appear in their names without prior
+ *    written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+                             /* ``It is hard to fly with
+                                  the eagles when you work
+                                  with the turkeys.''
+                                          -- Unknown  */
+#include "mod_ssl.h"
+
+
+/*  _________________________________________________________________
+**
+**  Expression Handling
+**  _________________________________________________________________
+*/
+
+ssl_expr_info_type ssl_expr_info;
+char              *ssl_expr_error;
+
+ssl_expr *ssl_expr_comp(pool *p, char *expr)
+{
+    ssl_expr_info.pool       = p;
+    ssl_expr_info.inputbuf   = expr;
+    ssl_expr_info.inputlen   = strlen(expr);
+    ssl_expr_info.inputptr   = ssl_expr_info.inputbuf;
+    ssl_expr_info.expr       = FALSE;
+
+    ssl_expr_error = NULL;
+    if (ssl_expr_yyparse())
+        return NULL;
+    return ssl_expr_info.expr;
+}
+
+char *ssl_expr_get_error(void)
+{
+    if (ssl_expr_error == NULL)
+        return "";
+    return ssl_expr_error;
+}
+
+ssl_expr *ssl_expr_make(ssl_expr_node_op op, void *a1, void *a2)
+{
+    ssl_expr *node;
+
+    node = (ssl_expr *)ap_palloc(ssl_expr_info.pool, sizeof(ssl_expr));
+    node->node_op   = op;
+    node->node_arg1 = (char *)a1;
+    node->node_arg2 = (char *)a2;
+    return node;
+}
+
+int ssl_expr_exec(request_rec *r, ssl_expr *expr)
+{
+    BOOL rc;
+
+    rc = ssl_expr_eval(r, expr);
+    if (ssl_expr_error != NULL)
+        return (-1);
+    else
+        return (rc ? 1 : 0);
+}
+
diff --git a/modules/ssl/ssl_expr.h b/modules/ssl/ssl_expr.h
new file mode 100644 (file)
index 0000000..419bb02
--- /dev/null
@@ -0,0 +1,139 @@
+/*                      _             _
+**  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
+** | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
+** | | | | | | (_) | (_| |   \__ \__ \ |  www.modssl.org
+** |_| |_| |_|\___/ \__,_|___|___/___/_|  ftp.modssl.org
+**                      |_____|
+**  ssl_expr.h
+**  Expression Handling (Header)
+*/
+
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ *    nor may "mod_ssl" appear in their names without prior
+ *    written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+                             /* ``May all your PUSHes be POPed.'' */
+
+#ifndef SSL_EXPR_H
+#define SSL_EXPR_H
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef TRUE
+#define TRUE !FALSE
+#endif
+
+#ifndef YY_NULL
+#define YY_NULL 0
+#endif
+
+#ifndef MIN
+#define MIN(a,b) (((a)<(b))?(a):(b))
+#endif
+
+#ifndef BOOL
+#define BOOL unsigned int
+#endif
+
+#ifndef NULL
+#define NULL (void *)0
+#endif
+
+#ifndef NUL
+#define NUL '\0'
+#endif
+
+#ifndef YYDEBUG
+#define YYDEBUG 0
+#endif
+
+typedef enum {
+    op_NOP, op_ListElement,
+    op_True, op_False, op_Not, op_Or, op_And, op_Comp,
+    op_EQ, op_NE, op_LT, op_LE, op_GT, op_GE, op_IN, op_REG, op_NRE,
+    op_Digit, op_String, op_Regex, op_Var, op_Func
+} ssl_expr_node_op;
+
+typedef struct {
+    ssl_expr_node_op node_op;
+    void *node_arg1;
+    void *node_arg2;
+} ssl_expr_node;
+
+typedef ssl_expr_node ssl_expr;
+
+typedef struct {
+       pool     *pool;
+    char     *inputbuf;
+    int       inputlen;
+    char     *inputptr;
+    ssl_expr *expr;
+} ssl_expr_info_type;
+
+extern ssl_expr_info_type ssl_expr_info;
+extern char *ssl_expr_error;
+
+#define yylval  ssl_expr_yylval
+#define yyerror ssl_expr_yyerror
+#define yyinput ssl_expr_yyinput
+
+extern int ssl_expr_yyparse(void);
+extern int ssl_expr_yyerror(char *);
+extern int ssl_expr_yylex(void);
+
+extern ssl_expr *ssl_expr_comp(pool *, char *);
+extern int       ssl_expr_exec(request_rec *, ssl_expr *);
+extern char     *ssl_expr_get_error(void);
+extern ssl_expr *ssl_expr_make(ssl_expr_node_op, void *, void *);
+extern BOOL      ssl_expr_eval(request_rec *, ssl_expr *);
+
+#endif /* SSL_EXPR_H */
diff --git a/modules/ssl/ssl_expr_eval.c b/modules/ssl/ssl_expr_eval.c
new file mode 100644 (file)
index 0000000..d8c5ea5
--- /dev/null
@@ -0,0 +1,282 @@
+/*                      _             _
+**  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
+** | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
+** | | | | | | (_) | (_| |   \__ \__ \ |  www.modssl.org
+** |_| |_| |_|\___/ \__,_|___|___/___/_|  ftp.modssl.org
+**                      |_____|
+**  ssl_expr_eval.c
+**  Expression Evaluation
+*/
+
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ *    nor may "mod_ssl" appear in their names without prior
+ *    written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+                             /* ``Make love,
+                                  not software!''
+                                        -- Unknown */
+#include "mod_ssl.h"
+
+
+/*  _________________________________________________________________
+**
+**  Expression Evaluation
+**  _________________________________________________________________
+*/
+
+static BOOL  ssl_expr_eval_comp(request_rec *, ssl_expr *);
+static char *ssl_expr_eval_word(request_rec *, ssl_expr *);
+static char *ssl_expr_eval_func_file(request_rec *, char *);
+static int   ssl_expr_eval_strcmplex(char *, char *);
+
+BOOL ssl_expr_eval(request_rec *r, ssl_expr *node)
+{
+    switch (node->node_op) {
+        case op_True: {
+            return TRUE;
+        }
+        case op_False: {
+            return FALSE;
+        }
+        case op_Not: {
+            ssl_expr *e = (ssl_expr *)node->node_arg1;
+            return (!ssl_expr_eval(r, e));
+        }
+        case op_Or: {
+            ssl_expr *e1 = (ssl_expr *)node->node_arg1;
+            ssl_expr *e2 = (ssl_expr *)node->node_arg2;
+            return (ssl_expr_eval(r, e1) || ssl_expr_eval(r, e2));
+        }
+        case op_And: {
+            ssl_expr *e1 = (ssl_expr *)node->node_arg1;
+            ssl_expr *e2 = (ssl_expr *)node->node_arg2;
+            return (ssl_expr_eval(r, e1) && ssl_expr_eval(r, e2));
+        }
+        case op_Comp: {
+            ssl_expr *e = (ssl_expr *)node->node_arg1;
+            return ssl_expr_eval_comp(r, e);
+        }
+        default: {
+            ssl_expr_error = "Internal evaluation error: Unknown expression node";
+            return FALSE;
+        }
+    }
+}
+
+static BOOL ssl_expr_eval_comp(request_rec *r, ssl_expr *node)
+{
+    switch (node->node_op) {
+        case op_EQ: {
+            ssl_expr *e1 = (ssl_expr *)node->node_arg1;
+            ssl_expr *e2 = (ssl_expr *)node->node_arg2;
+            return (strcmp(ssl_expr_eval_word(r, e1), ssl_expr_eval_word(r, e2)) == 0);
+        }
+        case op_NE: {
+            ssl_expr *e1 = (ssl_expr *)node->node_arg1;
+            ssl_expr *e2 = (ssl_expr *)node->node_arg2;
+            return (strcmp(ssl_expr_eval_word(r, e1), ssl_expr_eval_word(r, e2)) != 0);
+        }
+        case op_LT: {
+            ssl_expr *e1 = (ssl_expr *)node->node_arg1;
+            ssl_expr *e2 = (ssl_expr *)node->node_arg2;
+            return (ssl_expr_eval_strcmplex(ssl_expr_eval_word(r, e1), ssl_expr_eval_word(r, e2)) <  0);
+        }
+        case op_LE: {
+            ssl_expr *e1 = (ssl_expr *)node->node_arg1;
+            ssl_expr *e2 = (ssl_expr *)node->node_arg2;
+            return (ssl_expr_eval_strcmplex(ssl_expr_eval_word(r, e1), ssl_expr_eval_word(r, e2)) <= 0);
+        }
+        case op_GT: {
+            ssl_expr *e1 = (ssl_expr *)node->node_arg1;
+            ssl_expr *e2 = (ssl_expr *)node->node_arg2;
+            return (ssl_expr_eval_strcmplex(ssl_expr_eval_word(r, e1), ssl_expr_eval_word(r, e2)) >  0);
+        }
+        case op_GE: {
+            ssl_expr *e1 = (ssl_expr *)node->node_arg1;
+            ssl_expr *e2 = (ssl_expr *)node->node_arg2;
+            return (ssl_expr_eval_strcmplex(ssl_expr_eval_word(r, e1), ssl_expr_eval_word(r, e2)) >= 0);
+        }
+        case op_IN: {
+            ssl_expr *e1 = (ssl_expr *)node->node_arg1;
+            ssl_expr *e2 = (ssl_expr *)node->node_arg2;
+            ssl_expr *e3;
+            char *w1 = ssl_expr_eval_word(r, e1);
+            BOOL found = FALSE;
+            do {
+                e3 = (ssl_expr *)e2->node_arg1;
+                e2 = (ssl_expr *)e2->node_arg2;
+                if (strcmp(w1, ssl_expr_eval_word(r, e3)) == 0) {
+                    found = TRUE;
+                    break;
+                }
+            } while (e2 != NULL);
+            return found;
+        }
+        case op_REG: {
+            ssl_expr *e1;
+            ssl_expr *e2;
+            char *word;
+            regex_t *regex;
+
+            e1 = (ssl_expr *)node->node_arg1;
+            e2 = (ssl_expr *)node->node_arg2;
+            word = ssl_expr_eval_word(r, e1);
+            regex = (regex_t *)(e2->node_arg1);
+            return (regexec(regex, word, 0, NULL, 0) == 0);
+        }
+        case op_NRE: {
+            ssl_expr *e1;
+            ssl_expr *e2;
+            char *word;
+            regex_t *regex;
+
+            e1 = (ssl_expr *)node->node_arg1;
+            e2 = (ssl_expr *)node->node_arg2;
+            word = ssl_expr_eval_word(r, e1);
+            regex = (regex_t *)(e2->node_arg1);
+            return !(regexec(regex, word, 0, NULL, 0) == 0);
+        }
+        default: {
+            ssl_expr_error = "Internal evaluation error: Unknown expression node";
+            return FALSE;
+        }
+    }
+}
+
+static char *ssl_expr_eval_word(request_rec *r, ssl_expr *node)
+{
+    switch (node->node_op) {
+        case op_Digit: {
+            char *string = (char *)node->node_arg1;
+            return string;
+        }
+        case op_String: {
+            char *string = (char *)node->node_arg1;
+            return string;
+        }
+        case op_Var: {
+            char *var = (char *)node->node_arg1;
+            char *val = ssl_var_lookup(r->pool, r->server, r->connection, r, var);
+            return (val == NULL ? "" : val);
+        }
+        case op_Func: {
+            char *name = (char *)node->node_arg1;
+            ssl_expr *args = (ssl_expr *)node->node_arg2;
+            if (strEQ(name, "file"))
+                return ssl_expr_eval_func_file(r, (char *)(args->node_arg1));
+            else {
+                ssl_expr_error = "Internal evaluation error: Unknown function name";
+                return "";
+            }
+        }
+        default: {
+            ssl_expr_error = "Internal evaluation error: Unknown expression node";
+            return FALSE;
+        }
+    }
+}
+
+static char *ssl_expr_eval_func_file(request_rec *r, char *filename)
+{
+    FILE *fp;
+    char *buf;
+    int len;
+
+    if ((fp = ap_pfopen(r->pool, filename, "r")) == NULL) {
+        ssl_expr_error = "Cannot open file";
+        return "";
+    }
+    fseek(fp, 0, SEEK_END);
+    len = ftell(fp);
+    if (len == 0) {
+        buf = (char *)ap_palloc(r->pool, sizeof(char) * 1);
+        *buf = NUL;
+    }
+    else {
+        if ((buf = (char *)ap_palloc(r->pool, sizeof(char) * len+1)) == NULL) {
+            ssl_expr_error = "Cannot allocate memory";
+            ap_pfclose(r->pool, fp);
+            return "";
+        }
+        fseek(fp, 0, SEEK_SET);
+        if (fread(buf, len, 1, fp) == 0) {
+            ssl_expr_error = "Cannot read from file";
+            fclose(fp);
+            return ("");
+        }
+        buf[len] = NUL;
+    }
+    ap_pfclose(r->pool, fp);
+    return buf;
+}
+
+/* a variant of strcmp(3) which works correctly also for number strings */
+static int ssl_expr_eval_strcmplex(char *cpNum1, char *cpNum2)
+{
+    int i, n1, n2;
+
+    if (cpNum1 == NULL)
+        return -1;
+    if (cpNum2 == NULL)
+        return +1;
+    n1 = strlen(cpNum1);
+    n2 = strlen(cpNum2);
+    if (n1 > n2)
+        return 1;
+    if (n1 < n2)
+        return -1;
+    for (i = 0; i < n1; i++) {
+        if (cpNum1[i] > cpNum2[i])
+            return 1;
+        if (cpNum1[i] < cpNum2[i])
+            return -1;
+    }
+    return 0;
+}
+
diff --git a/modules/ssl/ssl_expr_parse.c b/modules/ssl/ssl_expr_parse.c
new file mode 100644 (file)
index 0000000..8e35553
--- /dev/null
@@ -0,0 +1,605 @@
+#ifndef lint
+static char const 
+ssl_expr_yyrcsid[] = "$FreeBSD: src/usr.bin/yacc/skeleton.c,v 1.28 2000/01/17 02:04:06 bde Exp $";
+#endif
+#include <stdlib.h>
+#define YYBYACC 1
+#define YYMAJOR 1
+#define YYMINOR 9
+#define YYLEX ssl_expr_yylex()
+#define YYEMPTY -1
+#define ssl_expr_yyclearin (ssl_expr_yychar=(YYEMPTY))
+#define ssl_expr_yyerrok (ssl_expr_yyerrflag=0)
+#define YYRECOVERING() (ssl_expr_yyerrflag!=0)
+static int ssl_expr_yygrowstack();
+#define YYPREFIX "ssl_expr_yy"
+#line 72 "ssl_expr_parse.y"
+#include "mod_ssl.h"
+#line 75 "ssl_expr_parse.y"
+typedef union {
+    char     *cpVal;
+    ssl_expr *exVal;
+} YYSTYPE;
+#line 24 "y.tab.c"
+#define YYERRCODE 256
+#define T_TRUE 257
+#define T_FALSE 258
+#define T_DIGIT 259
+#define T_ID 260
+#define T_STRING 261
+#define T_REGEX 262
+#define T_REGEX_I 263
+#define T_FUNC_FILE 264
+#define T_OP_EQ 265
+#define T_OP_NE 266
+#define T_OP_LT 267
+#define T_OP_LE 268
+#define T_OP_GT 269
+#define T_OP_GE 270
+#define T_OP_REG 271
+#define T_OP_NRE 272
+#define T_OP_IN 273
+#define T_OP_OR 274
+#define T_OP_AND 275
+#define T_OP_NOT 276
+const short ssl_expr_yylhs[] = {                                        -1,
+    0,    1,    1,    1,    1,    1,    1,    1,    2,    2,
+    2,    2,    2,    2,    2,    2,    2,    5,    5,    6,
+    6,    6,    6,    4,    4,    3,
+};
+const short ssl_expr_yylen[] = {                                         2,
+    1,    1,    1,    2,    3,    3,    1,    3,    3,    3,
+    3,    3,    3,    3,    5,    3,    3,    1,    3,    1,
+    1,    4,    1,    1,    1,    4,
+};
+const short ssl_expr_yydefred[] = {                                      0,
+    2,    3,   20,   21,    0,    0,    0,    0,    0,    0,
+    7,   23,    0,    0,    4,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    8,
+    0,    0,    6,    9,   10,   11,   12,   13,   14,   24,
+   25,   16,   17,    0,   26,   22,    0,   18,   15,    0,
+   19,
+};
+const short ssl_expr_yydgoto[] = {                                       9,
+   10,   11,   12,   42,   47,   13,
+};
+const short ssl_expr_yysindex[] = {                                    -37,
+    0,    0,    0,    0,  -35,  -37,  -37,  -99,    0, -247,
+    0,    0, -250, -229,    0,  -39, -227,  -37,  -37,  -33,
+  -33,  -33,  -33,  -33,  -33, -233, -233,  -89,   -6,    0,
+  -87, -239,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,  -33,    0,    0,  -38,    0,    0,  -33,
+    0,
+};
+const short ssl_expr_yyrindex[] = {                                      0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,   39,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    1,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,
+};
+const short ssl_expr_yygindex[] = {                                      0,
+    7,    0,    0,   13,    0,  -13,
+};
+#define YYTABLESIZE 275
+const short ssl_expr_yytable[] = {                                       8,
+    5,   30,    7,    8,   14,   50,   34,   35,   36,   37,
+   38,   39,   15,   16,   20,   21,   22,   23,   24,   25,
+   26,   27,   28,   17,   32,   33,   18,   19,   40,   41,
+   48,   29,   31,   44,   45,   19,   51,   46,    1,   43,
+    0,    5,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,   49,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    1,
+    2,    3,    0,    4,    0,    3,    5,    4,    0,    0,
+    5,    0,    0,    0,   18,   19,    0,    0,    6,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    5,
+};
+const short ssl_expr_yycheck[] = {                                      37,
+    0,   41,   40,   37,   40,   44,   20,   21,   22,   23,
+   24,   25,    6,    7,  265,  266,  267,  268,  269,  270,
+  271,  272,  273,  123,   18,   19,  274,  275,  262,  263,
+   44,  261,  260,  123,   41,  275,   50,  125,    0,   27,
+   -1,   41,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,  125,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  257,
+  258,  259,   -1,  261,   -1,  259,  264,  261,   -1,   -1,
+  264,   -1,   -1,   -1,  274,  275,   -1,   -1,  276,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,  274,
+};
+#define YYFINAL 9
+#ifndef YYDEBUG
+#define YYDEBUG 0
+#endif
+#define YYMAXTOKEN 276
+#if YYDEBUG
+const char * const ssl_expr_yyname[] = {
+"end-of-file",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,"'%'",0,0,"'('","')'",0,0,"','",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"'{'",0,"'}'",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"T_TRUE",
+"T_FALSE","T_DIGIT","T_ID","T_STRING","T_REGEX","T_REGEX_I","T_FUNC_FILE",
+"T_OP_EQ","T_OP_NE","T_OP_LT","T_OP_LE","T_OP_GT","T_OP_GE","T_OP_REG",
+"T_OP_NRE","T_OP_IN","T_OP_OR","T_OP_AND","T_OP_NOT",
+};
+const char * const ssl_expr_yyrule[] = {
+"$accept : root",
+"root : expr",
+"expr : T_TRUE",
+"expr : T_FALSE",
+"expr : T_OP_NOT expr",
+"expr : expr T_OP_OR expr",
+"expr : expr T_OP_AND expr",
+"expr : comparison",
+"expr : '(' expr ')'",
+"comparison : word T_OP_EQ word",
+"comparison : word T_OP_NE word",
+"comparison : word T_OP_LT word",
+"comparison : word T_OP_LE word",
+"comparison : word T_OP_GT word",
+"comparison : word T_OP_GE word",
+"comparison : word T_OP_IN '{' words '}'",
+"comparison : word T_OP_REG regex",
+"comparison : word T_OP_NRE regex",
+"words : word",
+"words : words ',' word",
+"word : T_DIGIT",
+"word : T_STRING",
+"word : '%' '{' T_ID '}'",
+"word : funccall",
+"regex : T_REGEX",
+"regex : T_REGEX_I",
+"funccall : T_FUNC_FILE '(' T_STRING ')'",
+};
+#endif
+#if YYDEBUG
+#include <stdio.h>
+#endif
+#ifdef YYSTACKSIZE
+#undef YYMAXDEPTH
+#define YYMAXDEPTH YYSTACKSIZE
+#else
+#ifdef YYMAXDEPTH
+#define YYSTACKSIZE YYMAXDEPTH
+#else
+#define YYSTACKSIZE 10000
+#define YYMAXDEPTH 10000
+#endif
+#endif
+#define YYINITSTACKSIZE 200
+int ssl_expr_yydebug;
+int ssl_expr_yynerrs;
+int ssl_expr_yyerrflag;
+int ssl_expr_yychar;
+short *ssl_expr_yyssp;
+YYSTYPE *ssl_expr_yyvsp;
+YYSTYPE ssl_expr_yyval;
+YYSTYPE ssl_expr_yylval;
+short *ssl_expr_yyss;
+short *ssl_expr_yysslim;
+YYSTYPE *ssl_expr_yyvs;
+int ssl_expr_yystacksize;
+#line 180 "ssl_expr_parse.y"
+
+int ssl_expr_yyerror(char *s)
+{
+    ssl_expr_error = s;
+    return 2;
+}
+
+#line 230 "y.tab.c"
+/* allocate initial stack or double stack size, up to YYMAXDEPTH */
+static int ssl_expr_yygrowstack()
+{
+    int newsize, i;
+    short *newss;
+    YYSTYPE *newvs;
+
+    if ((newsize = ssl_expr_yystacksize) == 0)
+        newsize = YYINITSTACKSIZE;
+    else if (newsize >= YYMAXDEPTH)
+        return -1;
+    else if ((newsize *= 2) > YYMAXDEPTH)
+        newsize = YYMAXDEPTH;
+    i = ssl_expr_yyssp - ssl_expr_yyss;
+    newss = ssl_expr_yyss ? (short *)realloc(ssl_expr_yyss, newsize * sizeof *newss) :
+      (short *)malloc(newsize * sizeof *newss);
+    if (newss == NULL)
+        return -1;
+    ssl_expr_yyss = newss;
+    ssl_expr_yyssp = newss + i;
+    newvs = ssl_expr_yyvs ? (YYSTYPE *)realloc(ssl_expr_yyvs, newsize * sizeof *newvs) :
+      (YYSTYPE *)malloc(newsize * sizeof *newvs);
+    if (newvs == NULL)
+        return -1;
+    ssl_expr_yyvs = newvs;
+    ssl_expr_yyvsp = newvs + i;
+    ssl_expr_yystacksize = newsize;
+    ssl_expr_yysslim = ssl_expr_yyss + newsize - 1;
+    return 0;
+}
+
+#define YYABORT goto ssl_expr_yyabort
+#define YYREJECT goto ssl_expr_yyabort
+#define YYACCEPT goto ssl_expr_yyaccept
+#define YYERROR goto ssl_expr_yyerrlab
+
+#ifndef YYPARSE_PARAM
+#if defined(__cplusplus) || __STDC__
+#define YYPARSE_PARAM_ARG void
+#define YYPARSE_PARAM_DECL
+#else  /* ! ANSI-C/C++ */
+#define YYPARSE_PARAM_ARG
+#define YYPARSE_PARAM_DECL
+#endif /* ANSI-C/C++ */
+#else  /* YYPARSE_PARAM */
+#ifndef YYPARSE_PARAM_TYPE
+#define YYPARSE_PARAM_TYPE void *
+#endif
+#if defined(__cplusplus) || __STDC__
+#define YYPARSE_PARAM_ARG YYPARSE_PARAM_TYPE YYPARSE_PARAM
+#define YYPARSE_PARAM_DECL
+#else  /* ! ANSI-C/C++ */
+#define YYPARSE_PARAM_ARG YYPARSE_PARAM
+#define YYPARSE_PARAM_DECL YYPARSE_PARAM_TYPE YYPARSE_PARAM;
+#endif /* ANSI-C/C++ */
+#endif /* ! YYPARSE_PARAM */
+
+int
+ssl_expr_yyparse (YYPARSE_PARAM_ARG)
+    YYPARSE_PARAM_DECL
+{
+    register int ssl_expr_yym, ssl_expr_yyn, ssl_expr_yystate;
+#if YYDEBUG
+    register const char *ssl_expr_yys;
+
+    if ((ssl_expr_yys = getenv("YYDEBUG")))
+    {
+        ssl_expr_yyn = *ssl_expr_yys;
+        if (ssl_expr_yyn >= '0' && ssl_expr_yyn <= '9')
+            ssl_expr_yydebug = ssl_expr_yyn - '0';
+    }
+#endif
+
+    ssl_expr_yynerrs = 0;
+    ssl_expr_yyerrflag = 0;
+    ssl_expr_yychar = (-1);
+
+    if (ssl_expr_yyss == NULL && ssl_expr_yygrowstack()) goto ssl_expr_yyoverflow;
+    ssl_expr_yyssp = ssl_expr_yyss;
+    ssl_expr_yyvsp = ssl_expr_yyvs;
+    *ssl_expr_yyssp = ssl_expr_yystate = 0;
+
+ssl_expr_yyloop:
+    if ((ssl_expr_yyn = ssl_expr_yydefred[ssl_expr_yystate])) goto ssl_expr_yyreduce;
+    if (ssl_expr_yychar < 0)
+    {
+        if ((ssl_expr_yychar = ssl_expr_yylex()) < 0) ssl_expr_yychar = 0;
+#if YYDEBUG
+        if (ssl_expr_yydebug)
+        {
+            ssl_expr_yys = 0;
+            if (ssl_expr_yychar <= YYMAXTOKEN) ssl_expr_yys = ssl_expr_yyname[ssl_expr_yychar];
+            if (!ssl_expr_yys) ssl_expr_yys = "illegal-symbol";
+            printf("%sdebug: state %d, reading %d (%s)\n",
+                    YYPREFIX, ssl_expr_yystate, ssl_expr_yychar, ssl_expr_yys);
+        }
+#endif
+    }
+    if ((ssl_expr_yyn = ssl_expr_yysindex[ssl_expr_yystate]) && (ssl_expr_yyn += ssl_expr_yychar) >= 0 &&
+            ssl_expr_yyn <= YYTABLESIZE && ssl_expr_yycheck[ssl_expr_yyn] == ssl_expr_yychar)
+    {
+#if YYDEBUG
+        if (ssl_expr_yydebug)
+            printf("%sdebug: state %d, shifting to state %d\n",
+                    YYPREFIX, ssl_expr_yystate, ssl_expr_yytable[ssl_expr_yyn]);
+#endif
+        if (ssl_expr_yyssp >= ssl_expr_yysslim && ssl_expr_yygrowstack())
+        {
+            goto ssl_expr_yyoverflow;
+        }
+        *++ssl_expr_yyssp = ssl_expr_yystate = ssl_expr_yytable[ssl_expr_yyn];
+        *++ssl_expr_yyvsp = ssl_expr_yylval;
+        ssl_expr_yychar = (-1);
+        if (ssl_expr_yyerrflag > 0)  --ssl_expr_yyerrflag;
+        goto ssl_expr_yyloop;
+    }
+    if ((ssl_expr_yyn = ssl_expr_yyrindex[ssl_expr_yystate]) && (ssl_expr_yyn += ssl_expr_yychar) >= 0 &&
+            ssl_expr_yyn <= YYTABLESIZE && ssl_expr_yycheck[ssl_expr_yyn] == ssl_expr_yychar)
+    {
+        ssl_expr_yyn = ssl_expr_yytable[ssl_expr_yyn];
+        goto ssl_expr_yyreduce;
+    }
+    if (ssl_expr_yyerrflag) goto ssl_expr_yyinrecovery;
+#if defined(lint) || defined(__GNUC__)
+    goto ssl_expr_yynewerror;
+#endif
+ssl_expr_yynewerror:
+    ssl_expr_yyerror("syntax error");
+#if defined(lint) || defined(__GNUC__)
+    goto ssl_expr_yyerrlab;
+#endif
+ssl_expr_yyerrlab:
+    ++ssl_expr_yynerrs;
+ssl_expr_yyinrecovery:
+    if (ssl_expr_yyerrflag < 3)
+    {
+        ssl_expr_yyerrflag = 3;
+        for (;;)
+        {
+            if ((ssl_expr_yyn = ssl_expr_yysindex[*ssl_expr_yyssp]) && (ssl_expr_yyn += YYERRCODE) >= 0 &&
+                    ssl_expr_yyn <= YYTABLESIZE && ssl_expr_yycheck[ssl_expr_yyn] == YYERRCODE)
+            {
+#if YYDEBUG
+                if (ssl_expr_yydebug)
+                    printf("%sdebug: state %d, error recovery shifting\
+ to state %d\n", YYPREFIX, *ssl_expr_yyssp, ssl_expr_yytable[ssl_expr_yyn]);
+#endif
+                if (ssl_expr_yyssp >= ssl_expr_yysslim && ssl_expr_yygrowstack())
+                {
+                    goto ssl_expr_yyoverflow;
+                }
+                *++ssl_expr_yyssp = ssl_expr_yystate = ssl_expr_yytable[ssl_expr_yyn];
+                *++ssl_expr_yyvsp = ssl_expr_yylval;
+                goto ssl_expr_yyloop;
+            }
+            else
+            {
+#if YYDEBUG
+                if (ssl_expr_yydebug)
+                    printf("%sdebug: error recovery discarding state %d\n",
+                            YYPREFIX, *ssl_expr_yyssp);
+#endif
+                if (ssl_expr_yyssp <= ssl_expr_yyss) goto ssl_expr_yyabort;
+                --ssl_expr_yyssp;
+                --ssl_expr_yyvsp;
+            }
+        }
+    }
+    else
+    {
+        if (ssl_expr_yychar == 0) goto ssl_expr_yyabort;
+#if YYDEBUG
+        if (ssl_expr_yydebug)
+        {
+            ssl_expr_yys = 0;
+            if (ssl_expr_yychar <= YYMAXTOKEN) ssl_expr_yys = ssl_expr_yyname[ssl_expr_yychar];
+            if (!ssl_expr_yys) ssl_expr_yys = "illegal-symbol";
+            printf("%sdebug: state %d, error recovery discards token %d (%s)\n",
+                    YYPREFIX, ssl_expr_yystate, ssl_expr_yychar, ssl_expr_yys);
+        }
+#endif
+        ssl_expr_yychar = (-1);
+        goto ssl_expr_yyloop;
+    }
+ssl_expr_yyreduce:
+#if YYDEBUG
+    if (ssl_expr_yydebug)
+        printf("%sdebug: state %d, reducing by rule %d (%s)\n",
+                YYPREFIX, ssl_expr_yystate, ssl_expr_yyn, ssl_expr_yyrule[ssl_expr_yyn]);
+#endif
+    ssl_expr_yym = ssl_expr_yylen[ssl_expr_yyn];
+    ssl_expr_yyval = ssl_expr_yyvsp[1-ssl_expr_yym];
+    switch (ssl_expr_yyn)
+    {
+case 1:
+#line 118 "ssl_expr_parse.y"
+{ ssl_expr_info.expr = ssl_expr_yyvsp[0].exVal; }
+break;
+case 2:
+#line 121 "ssl_expr_parse.y"
+{ ssl_expr_yyval.exVal = ssl_expr_make(op_True,  NULL, NULL); }
+break;
+case 3:
+#line 122 "ssl_expr_parse.y"
+{ ssl_expr_yyval.exVal = ssl_expr_make(op_False, NULL, NULL); }
+break;
+case 4:
+#line 123 "ssl_expr_parse.y"
+{ ssl_expr_yyval.exVal = ssl_expr_make(op_Not,   ssl_expr_yyvsp[0].exVal,   NULL); }
+break;
+case 5:
+#line 124 "ssl_expr_parse.y"
+{ ssl_expr_yyval.exVal = ssl_expr_make(op_Or,    ssl_expr_yyvsp[-2].exVal,   ssl_expr_yyvsp[0].exVal);   }
+break;
+case 6:
+#line 125 "ssl_expr_parse.y"
+{ ssl_expr_yyval.exVal = ssl_expr_make(op_And,   ssl_expr_yyvsp[-2].exVal,   ssl_expr_yyvsp[0].exVal);   }
+break;
+case 7:
+#line 126 "ssl_expr_parse.y"
+{ ssl_expr_yyval.exVal = ssl_expr_make(op_Comp,  ssl_expr_yyvsp[0].exVal,   NULL); }
+break;
+case 8:
+#line 127 "ssl_expr_parse.y"
+{ ssl_expr_yyval.exVal = ssl_expr_yyvsp[-1].exVal; }
+break;
+case 9:
+#line 130 "ssl_expr_parse.y"
+{ ssl_expr_yyval.exVal = ssl_expr_make(op_EQ,  ssl_expr_yyvsp[-2].exVal, ssl_expr_yyvsp[0].exVal); }
+break;
+case 10:
+#line 131 "ssl_expr_parse.y"
+{ ssl_expr_yyval.exVal = ssl_expr_make(op_NE,  ssl_expr_yyvsp[-2].exVal, ssl_expr_yyvsp[0].exVal); }
+break;
+case 11:
+#line 132 "ssl_expr_parse.y"
+{ ssl_expr_yyval.exVal = ssl_expr_make(op_LT,  ssl_expr_yyvsp[-2].exVal, ssl_expr_yyvsp[0].exVal); }
+break;
+case 12:
+#line 133 "ssl_expr_parse.y"
+{ ssl_expr_yyval.exVal = ssl_expr_make(op_LE,  ssl_expr_yyvsp[-2].exVal, ssl_expr_yyvsp[0].exVal); }
+break;
+case 13:
+#line 134 "ssl_expr_parse.y"
+{ ssl_expr_yyval.exVal = ssl_expr_make(op_GT,  ssl_expr_yyvsp[-2].exVal, ssl_expr_yyvsp[0].exVal); }
+break;
+case 14:
+#line 135 "ssl_expr_parse.y"
+{ ssl_expr_yyval.exVal = ssl_expr_make(op_GE,  ssl_expr_yyvsp[-2].exVal, ssl_expr_yyvsp[0].exVal); }
+break;
+case 15:
+#line 136 "ssl_expr_parse.y"
+{ ssl_expr_yyval.exVal = ssl_expr_make(op_IN,  ssl_expr_yyvsp[-4].exVal, ssl_expr_yyvsp[-1].exVal); }
+break;
+case 16:
+#line 137 "ssl_expr_parse.y"
+{ ssl_expr_yyval.exVal = ssl_expr_make(op_REG, ssl_expr_yyvsp[-2].exVal, ssl_expr_yyvsp[0].exVal); }
+break;
+case 17:
+#line 138 "ssl_expr_parse.y"
+{ ssl_expr_yyval.exVal = ssl_expr_make(op_NRE, ssl_expr_yyvsp[-2].exVal, ssl_expr_yyvsp[0].exVal); }
+break;
+case 18:
+#line 141 "ssl_expr_parse.y"
+{ ssl_expr_yyval.exVal = ssl_expr_make(op_ListElement, ssl_expr_yyvsp[0].exVal, NULL); }
+break;
+case 19:
+#line 142 "ssl_expr_parse.y"
+{ ssl_expr_yyval.exVal = ssl_expr_make(op_ListElement, ssl_expr_yyvsp[0].exVal, ssl_expr_yyvsp[-2].exVal);   }
+break;
+case 20:
+#line 145 "ssl_expr_parse.y"
+{ ssl_expr_yyval.exVal = ssl_expr_make(op_Digit,  ssl_expr_yyvsp[0].cpVal, NULL); }
+break;
+case 21:
+#line 146 "ssl_expr_parse.y"
+{ ssl_expr_yyval.exVal = ssl_expr_make(op_String, ssl_expr_yyvsp[0].cpVal, NULL); }
+break;
+case 22:
+#line 147 "ssl_expr_parse.y"
+{ ssl_expr_yyval.exVal = ssl_expr_make(op_Var,    ssl_expr_yyvsp[-1].cpVal, NULL); }
+break;
+case 23:
+#line 148 "ssl_expr_parse.y"
+{ ssl_expr_yyval.exVal = ssl_expr_yyvsp[0].exVal; }
+break;
+case 24:
+#line 151 "ssl_expr_parse.y"
+{ 
+                regex_t *regex;
+                if ((regex = ap_pregcomp(ssl_expr_info.pool, ssl_expr_yyvsp[0].cpVal, 
+                                         REG_EXTENDED|REG_NOSUB)) == NULL) {
+                    ssl_expr_error = "Failed to compile regular expression";
+                    YYERROR;
+                    regex = NULL;
+                }
+                ssl_expr_yyval.exVal = ssl_expr_make(op_Regex, regex, NULL);
+            }
+break;
+case 25:
+#line 161 "ssl_expr_parse.y"
+{
+                regex_t *regex;
+                if ((regex = ap_pregcomp(ssl_expr_info.pool, ssl_expr_yyvsp[0].cpVal, 
+                                         REG_EXTENDED|REG_NOSUB|REG_ICASE)) == NULL) {
+                    ssl_expr_error = "Failed to compile regular expression";
+                    YYERROR;
+                    regex = NULL;
+                }
+                ssl_expr_yyval.exVal = ssl_expr_make(op_Regex, regex, NULL);
+            }
+break;
+case 26:
+#line 173 "ssl_expr_parse.y"
+{ 
+               ssl_expr *args = ssl_expr_make(op_ListElement, ssl_expr_yyvsp[-1].cpVal, NULL);
+               ssl_expr_yyval.exVal = ssl_expr_make(op_Func, "file", args);
+            }
+break;
+#line 550 "y.tab.c"
+    }
+    ssl_expr_yyssp -= ssl_expr_yym;
+    ssl_expr_yystate = *ssl_expr_yyssp;
+    ssl_expr_yyvsp -= ssl_expr_yym;
+    ssl_expr_yym = ssl_expr_yylhs[ssl_expr_yyn];
+    if (ssl_expr_yystate == 0 && ssl_expr_yym == 0)
+    {
+#if YYDEBUG
+        if (ssl_expr_yydebug)
+            printf("%sdebug: after reduction, shifting from state 0 to\
+ state %d\n", YYPREFIX, YYFINAL);
+#endif
+        ssl_expr_yystate = YYFINAL;
+        *++ssl_expr_yyssp = YYFINAL;
+        *++ssl_expr_yyvsp = ssl_expr_yyval;
+        if (ssl_expr_yychar < 0)
+        {
+            if ((ssl_expr_yychar = ssl_expr_yylex()) < 0) ssl_expr_yychar = 0;
+#if YYDEBUG
+            if (ssl_expr_yydebug)
+            {
+                ssl_expr_yys = 0;
+                if (ssl_expr_yychar <= YYMAXTOKEN) ssl_expr_yys = ssl_expr_yyname[ssl_expr_yychar];
+                if (!ssl_expr_yys) ssl_expr_yys = "illegal-symbol";
+                printf("%sdebug: state %d, reading %d (%s)\n",
+                        YYPREFIX, YYFINAL, ssl_expr_yychar, ssl_expr_yys);
+            }
+#endif
+        }
+        if (ssl_expr_yychar == 0) goto ssl_expr_yyaccept;
+        goto ssl_expr_yyloop;
+    }
+    if ((ssl_expr_yyn = ssl_expr_yygindex[ssl_expr_yym]) && (ssl_expr_yyn += ssl_expr_yystate) >= 0 &&
+            ssl_expr_yyn <= YYTABLESIZE && ssl_expr_yycheck[ssl_expr_yyn] == ssl_expr_yystate)
+        ssl_expr_yystate = ssl_expr_yytable[ssl_expr_yyn];
+    else
+        ssl_expr_yystate = ssl_expr_yydgoto[ssl_expr_yym];
+#if YYDEBUG
+    if (ssl_expr_yydebug)
+        printf("%sdebug: after reduction, shifting from state %d \
+to state %d\n", YYPREFIX, *ssl_expr_yyssp, ssl_expr_yystate);
+#endif
+    if (ssl_expr_yyssp >= ssl_expr_yysslim && ssl_expr_yygrowstack())
+    {
+        goto ssl_expr_yyoverflow;
+    }
+    *++ssl_expr_yyssp = ssl_expr_yystate;
+    *++ssl_expr_yyvsp = ssl_expr_yyval;
+    goto ssl_expr_yyloop;
+ssl_expr_yyoverflow:
+    ssl_expr_yyerror("yacc stack overflow");
+ssl_expr_yyabort:
+    return (1);
+ssl_expr_yyaccept:
+    return (0);
+}
diff --git a/modules/ssl/ssl_expr_parse.h b/modules/ssl/ssl_expr_parse.h
new file mode 100644 (file)
index 0000000..618cacb
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef YYERRCODE
+#define YYERRCODE 256
+#endif
+
+#define T_TRUE 257
+#define T_FALSE 258
+#define T_DIGIT 259
+#define T_ID 260
+#define T_STRING 261
+#define T_REGEX 262
+#define T_REGEX_I 263
+#define T_FUNC_FILE 264
+#define T_OP_EQ 265
+#define T_OP_NE 266
+#define T_OP_LT 267
+#define T_OP_LE 268
+#define T_OP_GT 269
+#define T_OP_GE 270
+#define T_OP_REG 271
+#define T_OP_NRE 272
+#define T_OP_IN 273
+#define T_OP_OR 274
+#define T_OP_AND 275
+#define T_OP_NOT 276
+typedef union {
+    char     *cpVal;
+    ssl_expr *exVal;
+} YYSTYPE;
+extern YYSTYPE ssl_expr_yylval;
diff --git a/modules/ssl/ssl_expr_parse.y b/modules/ssl/ssl_expr_parse.y
new file mode 100644 (file)
index 0000000..1e3ad6e
--- /dev/null
@@ -0,0 +1,186 @@
+/*                      _             _ 
+**  _ __ ___   ___   __| |    ___ ___| |  
+** | '_ ` _ \ / _ \ / _` |   / __/ __| |  
+** | | | | | | (_) | (_| |   \__ \__ \ | mod_ssl - Apache Interface to OpenSSL
+** |_| |_| |_|\___/ \__,_|___|___/___/_| http://www.modssl.org/
+**                      |_____|         
+**  ssl_expr_parse.y
+**  Expression LR(1) Parser
+*/
+
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by 
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ *    nor may "mod_ssl" appear in their names without prior
+ *    written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by 
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+                             /* ``What you see is all you get.''
+                                                                     -- Brian Kernighan      */
+
+/*  _________________________________________________________________
+**
+**  Expression Parser
+**  _________________________________________________________________
+*/
+
+%{
+#include "mod_ssl.h"
+%}
+
+%union {
+    char     *cpVal;
+    ssl_expr *exVal;
+}
+
+%token  T_TRUE
+%token  T_FALSE
+
+%token  <cpVal> T_DIGIT
+%token  <cpVal> T_ID
+%token  <cpVal> T_STRING
+%token  <cpVal> T_REGEX
+%token  <cpVal> T_REGEX_I
+
+%token  T_FUNC_FILE
+
+%token  T_OP_EQ
+%token  T_OP_NE
+%token  T_OP_LT
+%token  T_OP_LE
+%token  T_OP_GT
+%token  T_OP_GE
+%token  T_OP_REG
+%token  T_OP_NRE
+%token  T_OP_IN
+
+%token  T_OP_OR
+%token  T_OP_AND
+%token  T_OP_NOT
+
+%left   T_OP_OR
+%left   T_OP_AND
+%left   T_OP_NOT
+
+%type   <exVal>   expr
+%type   <exVal>   comparison
+%type   <exVal>   funccall
+%type   <exVal>   regex
+%type   <exVal>   words
+%type   <exVal>   word
+
+%%
+
+root      : expr                         { ssl_expr_info.expr = $1; }
+          ;
+
+expr      : T_TRUE                       { $$ = ssl_expr_make(op_True,  NULL, NULL); }
+          | T_FALSE                      { $$ = ssl_expr_make(op_False, NULL, NULL); }
+          | T_OP_NOT expr                { $$ = ssl_expr_make(op_Not,   $2,   NULL); }
+          | expr T_OP_OR expr            { $$ = ssl_expr_make(op_Or,    $1,   $3);   }
+          | expr T_OP_AND expr           { $$ = ssl_expr_make(op_And,   $1,   $3);   }
+          | comparison                   { $$ = ssl_expr_make(op_Comp,  $1,   NULL); }
+          | '(' expr ')'                 { $$ = $2; }
+          ;
+
+comparison: word T_OP_EQ word            { $$ = ssl_expr_make(op_EQ,  $1, $3); }
+          | word T_OP_NE word            { $$ = ssl_expr_make(op_NE,  $1, $3); }
+          | word T_OP_LT word            { $$ = ssl_expr_make(op_LT,  $1, $3); }
+          | word T_OP_LE word            { $$ = ssl_expr_make(op_LE,  $1, $3); }
+          | word T_OP_GT word            { $$ = ssl_expr_make(op_GT,  $1, $3); }
+          | word T_OP_GE word            { $$ = ssl_expr_make(op_GE,  $1, $3); }
+          | word T_OP_IN '{' words '}'   { $$ = ssl_expr_make(op_IN,  $1, $4); }
+          | word T_OP_REG regex          { $$ = ssl_expr_make(op_REG, $1, $3); }
+          | word T_OP_NRE regex          { $$ = ssl_expr_make(op_NRE, $1, $3); }
+          ;
+
+words     : word                         { $$ = ssl_expr_make(op_ListElement, $1, NULL); }
+          | words ',' word               { $$ = ssl_expr_make(op_ListElement, $3, $1);   }
+          ;
+
+word      : T_DIGIT                      { $$ = ssl_expr_make(op_Digit,  $1, NULL); }
+          | T_STRING                     { $$ = ssl_expr_make(op_String, $1, NULL); }
+          | '%' '{' T_ID '}'             { $$ = ssl_expr_make(op_Var,    $3, NULL); }
+          | funccall                     { $$ = $1; }
+          ;
+
+regex     : T_REGEX { 
+                regex_t *regex;
+                if ((regex = ap_pregcomp(ssl_expr_info.pool, $1, 
+                                         REG_EXTENDED|REG_NOSUB)) == NULL) {
+                    ssl_expr_error = "Failed to compile regular expression";
+                    YYERROR;
+                    regex = NULL;
+                }
+                $$ = ssl_expr_make(op_Regex, regex, NULL);
+            }
+          | T_REGEX_I {
+                regex_t *regex;
+                if ((regex = ap_pregcomp(ssl_expr_info.pool, $1, 
+                                         REG_EXTENDED|REG_NOSUB|REG_ICASE)) == NULL) {
+                    ssl_expr_error = "Failed to compile regular expression";
+                    YYERROR;
+                    regex = NULL;
+                }
+                $$ = ssl_expr_make(op_Regex, regex, NULL);
+            }
+          ;
+
+funccall  : T_FUNC_FILE '(' T_STRING ')' { 
+               ssl_expr *args = ssl_expr_make(op_ListElement, $3, NULL);
+               $$ = ssl_expr_make(op_Func, "file", args);
+            }
+          ;
+
+%%
+
+int yyerror(char *s)
+{
+    ssl_expr_error = s;
+    return 2;
+}
+
diff --git a/modules/ssl/ssl_expr_scan.c b/modules/ssl/ssl_expr_scan.c
new file mode 100644 (file)
index 0000000..f3e9f9d
--- /dev/null
@@ -0,0 +1,2002 @@
+#define yy_create_buffer ssl_expr_yy_create_buffer
+#define yy_delete_buffer ssl_expr_yy_delete_buffer
+#define yy_scan_buffer ssl_expr_yy_scan_buffer
+#define yy_scan_string ssl_expr_yy_scan_string
+#define yy_scan_bytes ssl_expr_yy_scan_bytes
+#define yy_flex_debug ssl_expr_yy_flex_debug
+#define yy_init_buffer ssl_expr_yy_init_buffer
+#define yy_flush_buffer ssl_expr_yy_flush_buffer
+#define yy_load_buffer_state ssl_expr_yy_load_buffer_state
+#define yy_switch_to_buffer ssl_expr_yy_switch_to_buffer
+#define yyin ssl_expr_yyin
+#define yyleng ssl_expr_yyleng
+#define yylex ssl_expr_yylex
+#define yyout ssl_expr_yyout
+#define yyrestart ssl_expr_yyrestart
+#define yytext ssl_expr_yytext
+
+/* A lexical scanner generated by flex */
+
+/* Scanner skeleton version:
+ */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+
+#include <stdio.h>
+
+
+/* cfront 1.2 defines "c_plusplus" instead of "__cplusplus" */
+#ifdef c_plusplus
+#ifndef __cplusplus
+#define __cplusplus
+#endif
+#endif
+
+
+#ifdef __cplusplus
+
+#include <stdlib.h>
+#include <unistd.h>
+
+/* Use prototypes in function declarations. */
+#define YY_USE_PROTOS
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else  /* ! __cplusplus */
+
+#if __STDC__
+
+#define YY_USE_PROTOS
+#define YY_USE_CONST
+
+#endif /* __STDC__ */
+#endif /* ! __cplusplus */
+
+#ifdef __TURBOC__
+ #pragma warn -rch
+ #pragma warn -use
+#include <io.h>
+#include <stdlib.h>
+#define YY_USE_CONST
+#define YY_USE_PROTOS
+#endif
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+
+#ifdef YY_USE_PROTOS
+#define YY_PROTO(proto) proto
+#else
+#define YY_PROTO(proto) ()
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index.  If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* Enter a start condition.  This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN yy_start = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state.  The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START ((yy_start - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE yyrestart( yyin )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#define YY_BUF_SIZE 16384
+
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+
+extern int yyleng;
+extern FILE *yyin, *yyout;
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+/* The funky do-while in the following #define is used to turn the definition
+ * int a single C statement (which needs a semi-colon terminator).  This
+ * avoids problems with code like:
+ *
+ *     if ( condition_holds )
+ *             yyless( 5 );
+ *     else
+ *             do_something_else();
+ *
+ * Prior to using the do-while the compiler would get upset at the
+ * "else" because it interpreted the "if" statement as being all
+ * done when it reached the ';' after the yyless() call.
+ */
+
+/* Return all but the first 'n' matched characters back to the input stream. */
+
+#define yyless(n) \
+       do \
+               { \
+               /* Undo effects of setting up yytext. */ \
+               *yy_cp = yy_hold_char; \
+               YY_RESTORE_YY_MORE_OFFSET \
+               yy_c_buf_p = yy_cp = yy_bp + n - YY_MORE_ADJ; \
+               YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+               } \
+       while ( 0 )
+
+#define unput(c) yyunput( c, yytext_ptr )
+
+/* The following is because we cannot portably get our hands on size_t
+ * (without autoconf's help, which isn't available because we want
+ * flex-generated scanners to compile on their own).
+ */
+typedef unsigned int yy_size_t;
+
+
+struct yy_buffer_state
+       {
+       FILE *yy_input_file;
+
+       char *yy_ch_buf;                /* input buffer */
+       char *yy_buf_pos;               /* current position in input buffer */
+
+       /* Size of input buffer in bytes, not including room for EOB
+        * characters.
+        */
+       yy_size_t yy_buf_size;
+
+       /* Number of characters read into yy_ch_buf, not including EOB
+        * characters.
+        */
+       int yy_n_chars;
+
+       /* Whether we "own" the buffer - i.e., we know we created it,
+        * and can realloc() it to grow it, and should free() it to
+        * delete it.
+        */
+       int yy_is_our_buffer;
+
+       /* Whether this is an "interactive" input source; if so, and
+        * if we're using stdio for input, then we want to use getc()
+        * instead of fread(), to make sure we stop fetching input after
+        * each newline.
+        */
+       int yy_is_interactive;
+
+       /* Whether we're considered to be at the beginning of a line.
+        * If so, '^' rules will be active on the next match, otherwise
+        * not.
+        */
+       int yy_at_bol;
+
+       /* Whether to try to fill the input buffer when we reach the
+        * end of it.
+        */
+       int yy_fill_buffer;
+
+       int yy_buffer_status;
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+       /* When an EOF's been seen but there's still some text to process
+        * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+        * shouldn't try reading from the input source any more.  We might
+        * still have a bunch of tokens to match, though, because of
+        * possible backing-up.
+        *
+        * When we actually see the EOF, we change the status to "new"
+        * (via yyrestart()), so that the user can continue scanning by
+        * just pointing yyin at a new input file.
+        */
+#define YY_BUFFER_EOF_PENDING 2
+       };
+
+static YY_BUFFER_STATE yy_current_buffer = 0;
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ */
+#define YY_CURRENT_BUFFER yy_current_buffer
+
+
+/* yy_hold_char holds the character lost when yytext is formed. */
+static char yy_hold_char;
+
+static int yy_n_chars;         /* number of characters read into yy_ch_buf */
+
+
+int yyleng;
+
+/* Points to current character in buffer. */
+static char *yy_c_buf_p = (char *) 0;
+static int yy_init = 1;                /* whether we need to initialize */
+static int yy_start = 0;       /* start state number */
+
+/* Flag which is used to allow yywrap()'s to do buffer switches
+ * instead of setting up a fresh yyin.  A bit of a hack ...
+ */
+static int yy_did_buffer_switch_on_eof;
+
+void yyrestart YY_PROTO(( FILE *input_file ));
+
+void yy_switch_to_buffer YY_PROTO(( YY_BUFFER_STATE new_buffer ));
+void yy_load_buffer_state YY_PROTO(( void ));
+YY_BUFFER_STATE yy_create_buffer YY_PROTO(( FILE *file, int size ));
+void yy_delete_buffer YY_PROTO(( YY_BUFFER_STATE b ));
+void yy_init_buffer YY_PROTO(( YY_BUFFER_STATE b, FILE *file ));
+void yy_flush_buffer YY_PROTO(( YY_BUFFER_STATE b ));
+#define YY_FLUSH_BUFFER yy_flush_buffer( yy_current_buffer )
+
+YY_BUFFER_STATE yy_scan_buffer YY_PROTO(( char *base, yy_size_t size ));
+YY_BUFFER_STATE yy_scan_string YY_PROTO(( yyconst char *yy_str ));
+YY_BUFFER_STATE yy_scan_bytes YY_PROTO(( yyconst char *bytes, int len ));
+
+static void *yy_flex_alloc YY_PROTO(( yy_size_t ));
+static void *yy_flex_realloc YY_PROTO(( void *, yy_size_t ));
+static void yy_flex_free YY_PROTO(( void * ));
+
+#define yy_new_buffer yy_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+       { \
+       if ( ! yy_current_buffer ) \
+               yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \
+       yy_current_buffer->yy_is_interactive = is_interactive; \
+       }
+
+#define yy_set_bol(at_bol) \
+       { \
+       if ( ! yy_current_buffer ) \
+               yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \
+       yy_current_buffer->yy_at_bol = at_bol; \
+       }
+
+#define YY_AT_BOL() (yy_current_buffer->yy_at_bol)
+
+
+#define yywrap() 1
+#define YY_SKIP_YYWRAP
+typedef unsigned char YY_CHAR;
+FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0;
+typedef int yy_state_type;
+extern char *yytext;
+#define yytext_ptr yytext
+
+static yy_state_type yy_get_previous_state YY_PROTO(( void ));
+static yy_state_type yy_try_NUL_trans YY_PROTO(( yy_state_type current_state ));
+static int yy_get_next_buffer YY_PROTO(( void ));
+static void yy_fatal_error YY_PROTO(( yyconst char msg[] ));
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+       yytext_ptr = yy_bp; \
+       yyleng = (int) (yy_cp - yy_bp); \
+       yy_hold_char = *yy_cp; \
+       *yy_cp = '\0'; \
+       yy_c_buf_p = yy_cp;
+
+#define YY_NUM_RULES 46
+#define YY_END_OF_BUFFER 47
+static yyconst short int yy_accept[86] =
+    {   0,
+        0,    0,    0,    0,    0,    0,    0,    0,   47,   45,
+        1,   38,    2,   45,   43,   24,   45,   28,   44,   44,
+       44,   44,   44,   44,   44,   44,   44,   44,   44,   45,
+       13,    4,    3,   14,   16,   18,   17,    1,   22,   32,
+       34,   43,   26,   20,   31,   30,   44,   44,   19,   44,
+       44,   29,   27,   39,   25,   23,   15,   15,   21,   44,
+       35,   44,   36,   13,   12,    5,    6,   10,   11,    7,
+        8,    9,   33,   44,   44,   37,   44,    5,    6,   44,
+       40,   41,    5,   42,    0
+    } ;
+
+static yyconst int yy_ec[256] =
+    {   0,
+        1,    1,    1,    1,    1,    1,    1,    1,    2,    3,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    2,    4,    5,    1,    1,    1,    6,    1,    1,
+        1,    1,    1,    1,    7,    1,    1,    8,    8,    8,
+        8,    8,    8,    8,    8,    9,    9,    7,    1,   10,
+       11,   12,    1,    1,   13,   13,   13,   13,   13,   13,
+       13,   13,   13,   13,   13,   13,   13,   13,   13,   13,
+       13,   13,   13,   13,   13,   13,   13,   13,   13,   13,
+        1,   14,    1,    1,    7,    1,   15,   16,   13,   17,
+
+       18,   19,   20,   13,   21,   13,   13,   22,   23,   24,
+       25,   13,   26,   27,   28,   29,   30,   13,   13,   13,
+       13,   13,    1,   31,    1,   32,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1
+    } ;
+
+static yyconst int yy_meta[33] =
+    {   0,
+        1,    1,    2,    1,    3,    1,    4,    4,    4,    1,
+        1,    1,    4,    3,    4,    4,    4,    4,    4,    4,
+        4,    4,    4,    4,    4,    4,    4,    4,    4,    4,
+        1,    1
+    } ;
+
+static yyconst short int yy_base[93] =
+    {   0,
+        0,    0,   30,   31,    0,    0,   82,   81,  101,  142,
+       35,   28,  142,   94,   32,   88,   31,   87,    0,   69,
+       66,   28,   28,   67,   29,   63,   30,   63,   62,   57,
+        0,  142,  142,   88,  142,  142,  142,   48,  142,  142,
+      142,   44,  142,  142,  142,  142,    0,   70,    0,   64,
+       63,    0,    0,    0,    0,    0,  142,    0,    0,   55,
+        0,   46,  142,    0,  142,   53,   62,  142,  142,  142,
+      142,  142,    0,   44,   48,    0,   41,   70,   72,   38,
+        0,    0,   74,    0,  142,  117,  121,  125,   50,  129,
+      133,  137
+
+    } ;
+
+static yyconst short int yy_def[93] =
+    {   0,
+       85,    1,   86,   86,   87,   87,   88,   88,   85,   85,
+       85,   85,   85,   85,   85,   85,   85,   85,   89,   89,
+       89,   89,   89,   89,   89,   90,   89,   89,   89,   85,
+       91,   85,   85,   92,   85,   85,   85,   85,   85,   85,
+       85,   85,   85,   85,   85,   85,   89,   89,   89,   89,
+       89,   89,   89,   89,   89,   89,   85,   89,   89,   89,
+       89,   89,   85,   91,   85,   85,   85,   85,   85,   85,
+       85,   85,   89,   89,   89,   89,   89,   85,   85,   89,
+       89,   89,   85,   89,    0,   85,   85,   85,   85,   85,
+       85,   85
+
+    } ;
+
+static yyconst short int yy_nxt[175] =
+    {   0,
+       10,   11,   11,   12,   13,   14,   10,   15,   15,   16,
+       17,   18,   19,   10,   20,   19,   19,   21,   22,   23,
+       24,   25,   26,   27,   28,   19,   19,   19,   29,   19,
+       30,   10,   32,   32,   33,   33,   38,   38,   39,   42,
+       42,   44,   50,   34,   34,   52,   55,   59,   51,   38,
+       38,   42,   42,   47,   60,   84,   53,   56,   82,   40,
+       78,   79,   45,   57,   57,   81,   57,   57,   57,   79,
+       79,   80,   57,   57,   57,   77,   57,   83,   79,   79,
+       79,   79,   79,   76,   75,   74,   73,   63,   62,   61,
+       54,   49,   48,   57,   57,   66,   67,   46,   43,   41,
+
+       85,   37,   37,   68,   85,   85,   69,   85,   85,   85,
+       85,   70,   85,   85,   71,   85,   72,   31,   31,   31,
+       31,   35,   35,   35,   35,   36,   36,   36,   36,   58,
+       85,   58,   58,   64,   85,   85,   64,   65,   65,   65,
+       65,    9,   85,   85,   85,   85,   85,   85,   85,   85,
+       85,   85,   85,   85,   85,   85,   85,   85,   85,   85,
+       85,   85,   85,   85,   85,   85,   85,   85,   85,   85,
+       85,   85,   85,   85
+    } ;
+
+static yyconst short int yy_chk[175] =
+    {   0,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    3,    4,    3,    4,   11,   11,   12,   15,
+       15,   17,   22,    3,    4,   23,   25,   27,   22,   38,
+       38,   42,   42,   89,   27,   80,   23,   25,   77,   12,
+       66,   66,   17,   26,   26,   75,   26,   26,   26,   67,
+       67,   74,   26,   26,   26,   62,   26,   78,   78,   79,
+       79,   83,   83,   60,   51,   50,   48,   30,   29,   28,
+       24,   21,   20,   26,   26,   34,   34,   18,   16,   14,
+
+        9,    8,    7,   34,    0,    0,   34,    0,    0,    0,
+        0,   34,    0,    0,   34,    0,   34,   86,   86,   86,
+       86,   87,   87,   87,   87,   88,   88,   88,   88,   90,
+        0,   90,   90,   91,    0,    0,   91,   92,   92,   92,
+       92,   85,   85,   85,   85,   85,   85,   85,   85,   85,
+       85,   85,   85,   85,   85,   85,   85,   85,   85,   85,
+       85,   85,   85,   85,   85,   85,   85,   85,   85,   85,
+       85,   85,   85,   85
+    } ;
+
+static yy_state_type yy_last_accepting_state;
+static char *yy_last_accepting_cpos;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+char *yytext;
+#line 1 "ssl_expr_scan.l"
+#define INITIAL 0
+/*                      _             _ 
+**  _ __ ___   ___   __| |    ___ ___| |  
+** | '_ ` _ \ / _ \ / _` |   / __/ __| |  
+** | | | | | | (_) | (_| |   \__ \__ \ | mod_ssl - Apache Interface to OpenSSL
+** |_| |_| |_|\___/ \__,_|___|___/___/_| http://www.modssl.org/
+**                      |_____|         
+**  ssl_expr_scan.l
+**  Expression Scanner
+*/
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by 
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ *    nor may "mod_ssl" appear in their names without prior
+ *    written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by 
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+/* ``Killing for peace is 
+like fucking for virginity.''
+-- Unknown  */
+/*  _________________________________________________________________
+**
+**  Expression Scanner
+**  _________________________________________________________________
+*/
+#line 73 "ssl_expr_scan.l"
+#include "mod_ssl.h"
+
+#include "ssl_expr_parse.h"
+
+#define YY_NO_UNPUT 1
+int yyinput(char *buf, int max_size);
+
+#undef  YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+    (result = yyinput(buf, max_size))
+
+#define MAX_STR_LEN 2048
+/* %option stack */
+#define YY_NEVER_INTERACTIVE 1
+#define str 1
+
+#define regex 2
+#define regex_flags 3
+
+#line 537 "lex.ssl_expr_yy.c"
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int yywrap YY_PROTO(( void ));
+#else
+extern int yywrap YY_PROTO(( void ));
+#endif
+#endif
+
+#ifndef YY_NO_UNPUT
+static void yyunput YY_PROTO(( int c, char *buf_ptr ));
+#endif
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy YY_PROTO(( char *, yyconst char *, int ));
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen YY_PROTO(( yyconst char * ));
+#endif
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+static int yyinput YY_PROTO(( void ));
+#else
+static int input YY_PROTO(( void ));
+#endif
+#endif
+
+#if YY_STACK_USED
+static int yy_start_stack_ptr = 0;
+static int yy_start_stack_depth = 0;
+static int *yy_start_stack = 0;
+#ifndef YY_NO_PUSH_STATE
+static void yy_push_state YY_PROTO(( int new_state ));
+#endif
+#ifndef YY_NO_POP_STATE
+static void yy_pop_state YY_PROTO(( void ));
+#endif
+#ifndef YY_NO_TOP_STATE
+static int yy_top_state YY_PROTO(( void ));
+#endif
+
+#else
+#define YY_NO_PUSH_STATE 1
+#define YY_NO_POP_STATE 1
+#define YY_NO_TOP_STATE 1
+#endif
+
+#ifdef YY_MALLOC_DECL
+YY_MALLOC_DECL
+#else
+#if __STDC__
+#ifndef __cplusplus
+#include <stdlib.h>
+#endif
+#else
+/* Just try to get by without declaring the routines.  This will fail
+ * miserably on non-ANSI systems for which sizeof(size_t) != sizeof(int)
+ * or sizeof(void*) != sizeof(int).
+ */
+#endif
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO (void) fwrite( yytext, yyleng, 1, yyout )
+#endif
+
+/* Gets input and stuffs it into "buf".  number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+       if ( yy_current_buffer->yy_is_interactive ) \
+               { \
+               int c = '*', n; \
+               for ( n = 0; n < max_size && \
+                            (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
+                       buf[n] = (char) c; \
+               if ( c == '\n' ) \
+                       buf[n++] = (char) c; \
+               if ( c == EOF && ferror( yyin ) ) \
+                       YY_FATAL_ERROR( "input in flex scanner failed" ); \
+               result = n; \
+               } \
+       else if ( ((result = fread( buf, 1, max_size, yyin )) == 0) \
+                 && ferror( yyin ) ) \
+               YY_FATAL_ERROR( "input in flex scanner failed" );
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg )
+#endif
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL int yylex YY_PROTO(( void ))
+#endif
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+       YY_USER_ACTION
+
+YY_DECL
+       {
+       register yy_state_type yy_current_state;
+       register char *yy_cp, *yy_bp;
+       register int yy_act;
+
+#line 94 "ssl_expr_scan.l"
+
+  
+  char  caStr[MAX_STR_LEN];
+  char *cpStr = NULL;
+  char  caRegex[MAX_STR_LEN];
+  char *cpRegex = NULL;
+  char  cRegexDel = NUL;
+
+ /*
+  * Whitespaces
+  */
+#line 700 "lex.ssl_expr_yy.c"
+
+       if ( yy_init )
+               {
+               yy_init = 0;
+
+#ifdef YY_USER_INIT
+               YY_USER_INIT;
+#endif
+
+               if ( ! yy_start )
+                       yy_start = 1;   /* first start state */
+
+               if ( ! yyin )
+                       yyin = stdin;
+
+               if ( ! yyout )
+                       yyout = stdout;
+
+               if ( ! yy_current_buffer )
+                       yy_current_buffer =
+                               yy_create_buffer( yyin, YY_BUF_SIZE );
+
+               yy_load_buffer_state();
+               }
+
+       while ( 1 )             /* loops until end-of-file is reached */
+               {
+               yy_cp = yy_c_buf_p;
+
+               /* Support of yytext. */
+               *yy_cp = yy_hold_char;
+
+               /* yy_bp points to the position in yy_ch_buf of the start of
+                * the current run.
+                */
+               yy_bp = yy_cp;
+
+               yy_current_state = yy_start;
+yy_match:
+               do
+                       {
+                       register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
+                       if ( yy_accept[yy_current_state] )
+                               {
+                               yy_last_accepting_state = yy_current_state;
+                               yy_last_accepting_cpos = yy_cp;
+                               }
+                       while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+                               {
+                               yy_current_state = (int) yy_def[yy_current_state];
+                               if ( yy_current_state >= 86 )
+                                       yy_c = yy_meta[(unsigned int) yy_c];
+                               }
+                       yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+                       ++yy_cp;
+                       }
+               while ( yy_current_state != 85 );
+               yy_cp = yy_last_accepting_cpos;
+               yy_current_state = yy_last_accepting_state;
+
+yy_find_action:
+               yy_act = yy_accept[yy_current_state];
+
+               YY_DO_BEFORE_ACTION;
+
+
+do_action:     /* This label is used only to access EOF actions. */
+
+
+               switch ( yy_act )
+       { /* beginning of action switch */
+                       case 0: /* must back up */
+                       /* undo the effects of YY_DO_BEFORE_ACTION */
+                       *yy_cp = yy_hold_char;
+                       yy_cp = yy_last_accepting_cpos;
+                       yy_current_state = yy_last_accepting_state;
+                       goto yy_find_action;
+
+case 1:
+YY_RULE_SETUP
+#line 105 "ssl_expr_scan.l"
+{ 
+    /* NOP */
+}
+       YY_BREAK
+/*
+  * C-style strings ("...")
+  */
+case 2:
+YY_RULE_SETUP
+#line 112 "ssl_expr_scan.l"
+{
+    cpStr = caStr;
+    BEGIN(str);
+}
+       YY_BREAK
+case 3:
+YY_RULE_SETUP
+#line 116 "ssl_expr_scan.l"
+{
+    BEGIN(INITIAL);
+    *cpStr = NUL;
+    yylval.cpVal = ap_pstrdup(ssl_expr_info.pool, caStr);
+    return T_STRING;
+}
+       YY_BREAK
+case 4:
+YY_RULE_SETUP
+#line 122 "ssl_expr_scan.l"
+{
+    yyerror("Unterminated string");
+}
+       YY_BREAK
+case 5:
+YY_RULE_SETUP
+#line 125 "ssl_expr_scan.l"
+{
+    int result;
+
+    (void)sscanf(yytext+1, "%o", &result);
+    if (result > 0xff)
+        yyerror("Escape sequence out of bound");
+    else
+        *cpStr++ = result;
+}
+       YY_BREAK
+case 6:
+YY_RULE_SETUP
+#line 134 "ssl_expr_scan.l"
+{
+    yyerror("Bad escape sequence");
+}
+       YY_BREAK
+case 7:
+YY_RULE_SETUP
+#line 137 "ssl_expr_scan.l"
+{ *cpStr++ = '\n'; }
+       YY_BREAK
+case 8:
+YY_RULE_SETUP
+#line 138 "ssl_expr_scan.l"
+{ *cpStr++ = '\r'; }
+       YY_BREAK
+case 9:
+YY_RULE_SETUP
+#line 139 "ssl_expr_scan.l"
+{ *cpStr++ = '\t'; }
+       YY_BREAK
+case 10:
+YY_RULE_SETUP
+#line 140 "ssl_expr_scan.l"
+{ *cpStr++ = '\b'; }
+       YY_BREAK
+case 11:
+YY_RULE_SETUP
+#line 141 "ssl_expr_scan.l"
+{ *cpStr++ = '\f'; }
+       YY_BREAK
+case 12:
+YY_RULE_SETUP
+#line 142 "ssl_expr_scan.l"
+{
+    *cpStr++ = yytext[1];
+}
+       YY_BREAK
+case 13:
+YY_RULE_SETUP
+#line 145 "ssl_expr_scan.l"
+{
+    char *cp = yytext;
+    while (*cp != NUL)
+        *cpStr++ = *cp++;
+}
+       YY_BREAK
+case 14:
+YY_RULE_SETUP
+#line 150 "ssl_expr_scan.l"
+{
+    *cpStr++ = yytext[1];
+}
+       YY_BREAK
+/*
+  * Regular Expression
+  */
+case 15:
+YY_RULE_SETUP
+#line 157 "ssl_expr_scan.l"
+{
+    cRegexDel = yytext[1];
+    cpRegex = caRegex;
+    BEGIN(regex);
+}
+       YY_BREAK
+case 16:
+YY_RULE_SETUP
+#line 162 "ssl_expr_scan.l"
+{
+    if (yytext[0] == cRegexDel) {
+        *cpRegex = NUL;
+        BEGIN(regex_flags);
+    }
+    else {
+        *cpRegex++ = yytext[0];
+    }
+}
+       YY_BREAK
+case 17:
+YY_RULE_SETUP
+#line 171 "ssl_expr_scan.l"
+{
+    yylval.cpVal = ap_pstrdup(ssl_expr_info.pool, caRegex);
+    BEGIN(INITIAL);
+    return T_REGEX_I;
+}
+       YY_BREAK
+case 18:
+YY_RULE_SETUP
+#line 176 "ssl_expr_scan.l"
+{
+    yylval.cpVal = ap_pstrdup(ssl_expr_info.pool, caRegex);
+    yyless(0);
+    BEGIN(INITIAL);
+    return T_REGEX;
+}
+       YY_BREAK
+case YY_STATE_EOF(regex_flags):
+#line 182 "ssl_expr_scan.l"
+{
+    yylval.cpVal = ap_pstrdup(ssl_expr_info.pool, caRegex);
+    BEGIN(INITIAL);
+    return T_REGEX;
+}
+       YY_BREAK
+/*
+  * Operators
+  */
+case 19:
+YY_RULE_SETUP
+#line 191 "ssl_expr_scan.l"
+{ return T_OP_EQ; }
+       YY_BREAK
+case 20:
+YY_RULE_SETUP
+#line 192 "ssl_expr_scan.l"
+{ return T_OP_EQ; }
+       YY_BREAK
+case 21:
+YY_RULE_SETUP
+#line 193 "ssl_expr_scan.l"
+{ return T_OP_NE; }
+       YY_BREAK
+case 22:
+YY_RULE_SETUP
+#line 194 "ssl_expr_scan.l"
+{ return T_OP_NE; }
+       YY_BREAK
+case 23:
+YY_RULE_SETUP
+#line 195 "ssl_expr_scan.l"
+{ return T_OP_LT; }
+       YY_BREAK
+case 24:
+YY_RULE_SETUP
+#line 196 "ssl_expr_scan.l"
+{ return T_OP_LT; }
+       YY_BREAK
+case 25:
+YY_RULE_SETUP
+#line 197 "ssl_expr_scan.l"
+{ return T_OP_LE; }
+       YY_BREAK
+case 26:
+YY_RULE_SETUP
+#line 198 "ssl_expr_scan.l"
+{ return T_OP_LE; }
+       YY_BREAK
+case 27:
+YY_RULE_SETUP
+#line 199 "ssl_expr_scan.l"
+{ return T_OP_GT; }
+       YY_BREAK
+case 28:
+YY_RULE_SETUP
+#line 200 "ssl_expr_scan.l"
+{ return T_OP_GT; }
+       YY_BREAK
+case 29:
+YY_RULE_SETUP
+#line 201 "ssl_expr_scan.l"
+{ return T_OP_GE; }
+       YY_BREAK
+case 30:
+YY_RULE_SETUP
+#line 202 "ssl_expr_scan.l"
+{ return T_OP_GE; }
+       YY_BREAK
+case 31:
+YY_RULE_SETUP
+#line 203 "ssl_expr_scan.l"
+{ return T_OP_REG; }
+       YY_BREAK
+case 32:
+YY_RULE_SETUP
+#line 204 "ssl_expr_scan.l"
+{ return T_OP_NRE; }
+       YY_BREAK
+case 33:
+YY_RULE_SETUP
+#line 205 "ssl_expr_scan.l"
+{ return T_OP_AND; }
+       YY_BREAK
+case 34:
+YY_RULE_SETUP
+#line 206 "ssl_expr_scan.l"
+{ return T_OP_AND; }
+       YY_BREAK
+case 35:
+YY_RULE_SETUP
+#line 207 "ssl_expr_scan.l"
+{ return T_OP_OR; }
+       YY_BREAK
+case 36:
+YY_RULE_SETUP
+#line 208 "ssl_expr_scan.l"
+{ return T_OP_OR; }
+       YY_BREAK
+case 37:
+YY_RULE_SETUP
+#line 209 "ssl_expr_scan.l"
+{ return T_OP_NOT; }
+       YY_BREAK
+case 38:
+YY_RULE_SETUP
+#line 210 "ssl_expr_scan.l"
+{ return T_OP_NOT; }
+       YY_BREAK
+case 39:
+YY_RULE_SETUP
+#line 211 "ssl_expr_scan.l"
+{ return T_OP_IN; }
+       YY_BREAK
+/*
+  * Functions
+  */
+case 40:
+YY_RULE_SETUP
+#line 216 "ssl_expr_scan.l"
+{ return T_FUNC_FILE; }
+       YY_BREAK
+/*
+  * Specials
+  */
+case 41:
+YY_RULE_SETUP
+#line 221 "ssl_expr_scan.l"
+{ return T_TRUE; }
+       YY_BREAK
+case 42:
+YY_RULE_SETUP
+#line 222 "ssl_expr_scan.l"
+{ return T_FALSE; }
+       YY_BREAK
+/*
+  * Digits
+  */
+case 43:
+YY_RULE_SETUP
+#line 227 "ssl_expr_scan.l"
+{
+    yylval.cpVal = ap_pstrdup(ssl_expr_info.pool, yytext);
+    return T_DIGIT;
+}
+       YY_BREAK
+/*
+  * Identifiers
+  */
+case 44:
+YY_RULE_SETUP
+#line 235 "ssl_expr_scan.l"
+{
+    yylval.cpVal = ap_pstrdup(ssl_expr_info.pool, yytext);
+    return T_ID;
+}
+       YY_BREAK
+/*
+  * Anything else is returned as is...
+  */
+case 45:
+YY_RULE_SETUP
+#line 243 "ssl_expr_scan.l"
+{ 
+    return yytext[0];
+}
+       YY_BREAK
+case 46:
+YY_RULE_SETUP
+#line 247 "ssl_expr_scan.l"
+YY_FATAL_ERROR( "flex scanner jammed" );
+       YY_BREAK
+#line 1100 "lex.ssl_expr_yy.c"
+case YY_STATE_EOF(INITIAL):
+case YY_STATE_EOF(str):
+case YY_STATE_EOF(regex):
+       yyterminate();
+
+       case YY_END_OF_BUFFER:
+               {
+               /* Amount of text matched not including the EOB char. */
+               int yy_amount_of_matched_text = (int) (yy_cp - yytext_ptr) - 1;
+
+               /* Undo the effects of YY_DO_BEFORE_ACTION. */
+               *yy_cp = yy_hold_char;
+               YY_RESTORE_YY_MORE_OFFSET
+
+               if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_NEW )
+                       {
+                       /* We're scanning a new file or input source.  It's
+                        * possible that this happened because the user
+                        * just pointed yyin at a new source and called
+                        * yylex().  If so, then we have to assure
+                        * consistency between yy_current_buffer and our
+                        * globals.  Here is the right place to do so, because
+                        * this is the first action (other than possibly a
+                        * back-up) that will match for the new input source.
+                        */
+                       yy_n_chars = yy_current_buffer->yy_n_chars;
+                       yy_current_buffer->yy_input_file = yyin;
+                       yy_current_buffer->yy_buffer_status = YY_BUFFER_NORMAL;
+                       }
+
+               /* Note that here we test for yy_c_buf_p "<=" to the position
+                * of the first EOB in the buffer, since yy_c_buf_p will
+                * already have been incremented past the NUL character
+                * (since all states make transitions on EOB to the
+                * end-of-buffer state).  Contrast this with the test
+                * in input().
+                */
+               if ( yy_c_buf_p <= &yy_current_buffer->yy_ch_buf[yy_n_chars] )
+                       { /* This was really a NUL. */
+                       yy_state_type yy_next_state;
+
+                       yy_c_buf_p = yytext_ptr + yy_amount_of_matched_text;
+
+                       yy_current_state = yy_get_previous_state();
+
+                       /* Okay, we're now positioned to make the NUL
+                        * transition.  We couldn't have
+                        * yy_get_previous_state() go ahead and do it
+                        * for us because it doesn't know how to deal
+                        * with the possibility of jamming (and we don't
+                        * want to build jamming into it because then it
+                        * will run more slowly).
+                        */
+
+                       yy_next_state = yy_try_NUL_trans( yy_current_state );
+
+                       yy_bp = yytext_ptr + YY_MORE_ADJ;
+
+                       if ( yy_next_state )
+                               {
+                               /* Consume the NUL. */
+                               yy_cp = ++yy_c_buf_p;
+                               yy_current_state = yy_next_state;
+                               goto yy_match;
+                               }
+
+                       else
+                               {
+                               yy_cp = yy_last_accepting_cpos;
+                               yy_current_state = yy_last_accepting_state;
+                               goto yy_find_action;
+                               }
+                       }
+
+               else switch ( yy_get_next_buffer() )
+                       {
+                       case EOB_ACT_END_OF_FILE:
+                               {
+                               yy_did_buffer_switch_on_eof = 0;
+
+                               if ( yywrap() )
+                                       {
+                                       /* Note: because we've taken care in
+                                        * yy_get_next_buffer() to have set up
+                                        * yytext, we can now set up
+                                        * yy_c_buf_p so that if some total
+                                        * hoser (like flex itself) wants to
+                                        * call the scanner after we return the
+                                        * YY_NULL, it'll still work - another
+                                        * YY_NULL will get returned.
+                                        */
+                                       yy_c_buf_p = yytext_ptr + YY_MORE_ADJ;
+
+                                       yy_act = YY_STATE_EOF(YY_START);
+                                       goto do_action;
+                                       }
+
+                               else
+                                       {
+                                       if ( ! yy_did_buffer_switch_on_eof )
+                                               YY_NEW_FILE;
+                                       }
+                               break;
+                               }
+
+                       case EOB_ACT_CONTINUE_SCAN:
+                               yy_c_buf_p =
+                                       yytext_ptr + yy_amount_of_matched_text;
+
+                               yy_current_state = yy_get_previous_state();
+
+                               yy_cp = yy_c_buf_p;
+                               yy_bp = yytext_ptr + YY_MORE_ADJ;
+                               goto yy_match;
+
+                       case EOB_ACT_LAST_MATCH:
+                               yy_c_buf_p =
+                               &yy_current_buffer->yy_ch_buf[yy_n_chars];
+
+                               yy_current_state = yy_get_previous_state();
+
+                               yy_cp = yy_c_buf_p;
+                               yy_bp = yytext_ptr + YY_MORE_ADJ;
+                               goto yy_find_action;
+                       }
+               break;
+               }
+
+       default:
+               YY_FATAL_ERROR(
+                       "fatal flex scanner internal error--no action found" );
+       } /* end of action switch */
+               } /* end of scanning one token */
+       } /* end of yylex */
+
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ *     EOB_ACT_LAST_MATCH -
+ *     EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ *     EOB_ACT_END_OF_FILE - end of file
+ */
+
+static int yy_get_next_buffer()
+       {
+       register char *dest = yy_current_buffer->yy_ch_buf;
+       register char *source = yytext_ptr;
+       register int number_to_move, i;
+       int ret_val;
+
+       if ( yy_c_buf_p > &yy_current_buffer->yy_ch_buf[yy_n_chars + 1] )
+               YY_FATAL_ERROR(
+               "fatal flex scanner internal error--end of buffer missed" );
+
+       if ( yy_current_buffer->yy_fill_buffer == 0 )
+               { /* Don't try to fill the buffer, so this is an EOF. */
+               if ( yy_c_buf_p - yytext_ptr - YY_MORE_ADJ == 1 )
+                       {
+                       /* We matched a single character, the EOB, so
+                        * treat this as a final EOF.
+                        */
+                       return EOB_ACT_END_OF_FILE;
+                       }
+
+               else
+                       {
+                       /* We matched some text prior to the EOB, first
+                        * process it.
+                        */
+                       return EOB_ACT_LAST_MATCH;
+                       }
+               }
+
+       /* Try to read more data. */
+
+       /* First move last chars to start of buffer. */
+       number_to_move = (int) (yy_c_buf_p - yytext_ptr) - 1;
+
+       for ( i = 0; i < number_to_move; ++i )
+               *(dest++) = *(source++);
+
+       if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+               /* don't do the read, it's not guaranteed to return an EOF,
+                * just force an EOF
+                */
+               yy_current_buffer->yy_n_chars = yy_n_chars = 0;
+
+       else
+               {
+               int num_to_read =
+                       yy_current_buffer->yy_buf_size - number_to_move - 1;
+
+               while ( num_to_read <= 0 )
+                       { /* Not enough room in the buffer - grow it. */
+#ifdef YY_USES_REJECT
+                       YY_FATAL_ERROR(
+"input buffer overflow, can't enlarge buffer because scanner uses REJECT" );
+#else
+
+                       /* just a shorter name for the current buffer */
+                       YY_BUFFER_STATE b = yy_current_buffer;
+
+                       int yy_c_buf_p_offset =
+                               (int) (yy_c_buf_p - b->yy_ch_buf);
+
+                       if ( b->yy_is_our_buffer )
+                               {
+                               int new_size = b->yy_buf_size * 2;
+
+                               if ( new_size <= 0 )
+                                       b->yy_buf_size += b->yy_buf_size / 8;
+                               else
+                                       b->yy_buf_size *= 2;
+
+                               b->yy_ch_buf = (char *)
+                                       /* Include room in for 2 EOB chars. */
+                                       yy_flex_realloc( (void *) b->yy_ch_buf,
+                                                        b->yy_buf_size + 2 );
+                               }
+                       else
+                               /* Can't grow it, we don't own it. */
+                               b->yy_ch_buf = 0;
+
+                       if ( ! b->yy_ch_buf )
+                               YY_FATAL_ERROR(
+                               "fatal error - scanner input buffer overflow" );
+
+                       yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+                       num_to_read = yy_current_buffer->yy_buf_size -
+                                               number_to_move - 1;
+#endif
+                       }
+
+               if ( num_to_read > YY_READ_BUF_SIZE )
+                       num_to_read = YY_READ_BUF_SIZE;
+
+               /* Read in more data. */
+               YY_INPUT( (&yy_current_buffer->yy_ch_buf[number_to_move]),
+                       yy_n_chars, num_to_read );
+
+               yy_current_buffer->yy_n_chars = yy_n_chars;
+               }
+
+       if ( yy_n_chars == 0 )
+               {
+               if ( number_to_move == YY_MORE_ADJ )
+                       {
+                       ret_val = EOB_ACT_END_OF_FILE;
+                       yyrestart( yyin );
+                       }
+
+               else
+                       {
+                       ret_val = EOB_ACT_LAST_MATCH;
+                       yy_current_buffer->yy_buffer_status =
+                               YY_BUFFER_EOF_PENDING;
+                       }
+               }
+
+       else
+               ret_val = EOB_ACT_CONTINUE_SCAN;
+
+       yy_n_chars += number_to_move;
+       yy_current_buffer->yy_ch_buf[yy_n_chars] = YY_END_OF_BUFFER_CHAR;
+       yy_current_buffer->yy_ch_buf[yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR;
+
+       yytext_ptr = &yy_current_buffer->yy_ch_buf[0];
+
+       return ret_val;
+       }
+
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+static yy_state_type yy_get_previous_state()
+       {
+       register yy_state_type yy_current_state;
+       register char *yy_cp;
+
+       yy_current_state = yy_start;
+
+       for ( yy_cp = yytext_ptr + YY_MORE_ADJ; yy_cp < yy_c_buf_p; ++yy_cp )
+               {
+               register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+               if ( yy_accept[yy_current_state] )
+                       {
+                       yy_last_accepting_state = yy_current_state;
+                       yy_last_accepting_cpos = yy_cp;
+                       }
+               while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+                       {
+                       yy_current_state = (int) yy_def[yy_current_state];
+                       if ( yy_current_state >= 86 )
+                               yy_c = yy_meta[(unsigned int) yy_c];
+                       }
+               yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+               }
+
+       return yy_current_state;
+       }
+
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ *     next_state = yy_try_NUL_trans( current_state );
+ */
+
+#ifdef YY_USE_PROTOS
+static yy_state_type yy_try_NUL_trans( yy_state_type yy_current_state )
+#else
+static yy_state_type yy_try_NUL_trans( yy_current_state )
+yy_state_type yy_current_state;
+#endif
+       {
+       register int yy_is_jam;
+       register char *yy_cp = yy_c_buf_p;
+
+       register YY_CHAR yy_c = 1;
+       if ( yy_accept[yy_current_state] )
+               {
+               yy_last_accepting_state = yy_current_state;
+               yy_last_accepting_cpos = yy_cp;
+               }
+       while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+               {
+               yy_current_state = (int) yy_def[yy_current_state];
+               if ( yy_current_state >= 86 )
+                       yy_c = yy_meta[(unsigned int) yy_c];
+               }
+       yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+       yy_is_jam = (yy_current_state == 85);
+
+       return yy_is_jam ? 0 : yy_current_state;
+       }
+
+
+#ifndef YY_NO_UNPUT
+#ifdef YY_USE_PROTOS
+static void yyunput( int c, register char *yy_bp )
+#else
+static void yyunput( c, yy_bp )
+int c;
+register char *yy_bp;
+#endif
+       {
+       register char *yy_cp = yy_c_buf_p;
+
+       /* undo effects of setting up yytext */
+       *yy_cp = yy_hold_char;
+
+       if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 )
+               { /* need to shift things up to make room */
+               /* +2 for EOB chars. */
+               register int number_to_move = yy_n_chars + 2;
+               register char *dest = &yy_current_buffer->yy_ch_buf[
+                                       yy_current_buffer->yy_buf_size + 2];
+               register char *source =
+                               &yy_current_buffer->yy_ch_buf[number_to_move];
+
+               while ( source > yy_current_buffer->yy_ch_buf )
+                       *--dest = *--source;
+
+               yy_cp += (int) (dest - source);
+               yy_bp += (int) (dest - source);
+               yy_current_buffer->yy_n_chars =
+                       yy_n_chars = yy_current_buffer->yy_buf_size;
+
+               if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 )
+                       YY_FATAL_ERROR( "flex scanner push-back overflow" );
+               }
+
+       *--yy_cp = (char) c;
+
+
+       yytext_ptr = yy_bp;
+       yy_hold_char = *yy_cp;
+       yy_c_buf_p = yy_cp;
+       }
+#endif /* ifndef YY_NO_UNPUT */
+
+
+#ifdef __cplusplus
+static int yyinput()
+#else
+static int input()
+#endif
+       {
+       int c;
+
+       *yy_c_buf_p = yy_hold_char;
+
+       if ( *yy_c_buf_p == YY_END_OF_BUFFER_CHAR )
+               {
+               /* yy_c_buf_p now points to the character we want to return.
+                * If this occurs *before* the EOB characters, then it's a
+                * valid NUL; if not, then we've hit the end of the buffer.
+                */
+               if ( yy_c_buf_p < &yy_current_buffer->yy_ch_buf[yy_n_chars] )
+                       /* This was really a NUL. */
+                       *yy_c_buf_p = '\0';
+
+               else
+                       { /* need more input */
+                       int offset = yy_c_buf_p - yytext_ptr;
+                       ++yy_c_buf_p;
+
+                       switch ( yy_get_next_buffer() )
+                               {
+                               case EOB_ACT_LAST_MATCH:
+                                       /* This happens because yy_g_n_b()
+                                        * sees that we've accumulated a
+                                        * token and flags that we need to
+                                        * try matching the token before
+                                        * proceeding.  But for input(),
+                                        * there's no matching to consider.
+                                        * So convert the EOB_ACT_LAST_MATCH
+                                        * to EOB_ACT_END_OF_FILE.
+                                        */
+
+                                       /* Reset buffer status. */
+                                       yyrestart( yyin );
+
+                                       /* fall through */
+
+                               case EOB_ACT_END_OF_FILE:
+                                       {
+                                       if ( yywrap() )
+                                               return EOF;
+
+                                       if ( ! yy_did_buffer_switch_on_eof )
+                                               YY_NEW_FILE;
+#ifdef __cplusplus
+                                       return yyinput();
+#else
+                                       return input();
+#endif
+                                       }
+
+                               case EOB_ACT_CONTINUE_SCAN:
+                                       yy_c_buf_p = yytext_ptr + offset;
+                                       break;
+                               }
+                       }
+               }
+
+       c = *(unsigned char *) yy_c_buf_p;      /* cast for 8-bit char's */
+       *yy_c_buf_p = '\0';     /* preserve yytext */
+       yy_hold_char = *++yy_c_buf_p;
+
+
+       return c;
+       }
+
+
+#ifdef YY_USE_PROTOS
+void yyrestart( FILE *input_file )
+#else
+void yyrestart( input_file )
+FILE *input_file;
+#endif
+       {
+       if ( ! yy_current_buffer )
+               yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE );
+
+       yy_init_buffer( yy_current_buffer, input_file );
+       yy_load_buffer_state();
+       }
+
+
+#ifdef YY_USE_PROTOS
+void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer )
+#else
+void yy_switch_to_buffer( new_buffer )
+YY_BUFFER_STATE new_buffer;
+#endif
+       {
+       if ( yy_current_buffer == new_buffer )
+               return;
+
+       if ( yy_current_buffer )
+               {
+               /* Flush out information for old buffer. */
+               *yy_c_buf_p = yy_hold_char;
+               yy_current_buffer->yy_buf_pos = yy_c_buf_p;
+               yy_current_buffer->yy_n_chars = yy_n_chars;
+               }
+
+       yy_current_buffer = new_buffer;
+       yy_load_buffer_state();
+
+       /* We don't actually know whether we did this switch during
+        * EOF (yywrap()) processing, but the only time this flag
+        * is looked at is after yywrap() is called, so it's safe
+        * to go ahead and always set it.
+        */
+       yy_did_buffer_switch_on_eof = 1;
+       }
+
+
+#ifdef YY_USE_PROTOS
+void yy_load_buffer_state( void )
+#else
+void yy_load_buffer_state()
+#endif
+       {
+       yy_n_chars = yy_current_buffer->yy_n_chars;
+       yytext_ptr = yy_c_buf_p = yy_current_buffer->yy_buf_pos;
+       yyin = yy_current_buffer->yy_input_file;
+       yy_hold_char = *yy_c_buf_p;
+       }
+
+
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_create_buffer( FILE *file, int size )
+#else
+YY_BUFFER_STATE yy_create_buffer( file, size )
+FILE *file;
+int size;
+#endif
+       {
+       YY_BUFFER_STATE b;
+
+       b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) );
+       if ( ! b )
+               YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+       b->yy_buf_size = size;
+
+       /* yy_ch_buf has to be 2 characters longer than the size given because
+        * we need to put in 2 end-of-buffer characters.
+        */
+       b->yy_ch_buf = (char *) yy_flex_alloc( b->yy_buf_size + 2 );
+       if ( ! b->yy_ch_buf )
+               YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+       b->yy_is_our_buffer = 1;
+
+       yy_init_buffer( b, file );
+
+       return b;
+       }
+
+
+#ifdef YY_USE_PROTOS
+void yy_delete_buffer( YY_BUFFER_STATE b )
+#else
+void yy_delete_buffer( b )
+YY_BUFFER_STATE b;
+#endif
+       {
+       if ( ! b )
+               return;
+
+       if ( b == yy_current_buffer )
+               yy_current_buffer = (YY_BUFFER_STATE) 0;
+
+       if ( b->yy_is_our_buffer )
+               yy_flex_free( (void *) b->yy_ch_buf );
+
+       yy_flex_free( (void *) b );
+       }
+
+
+#ifndef YY_ALWAYS_INTERACTIVE
+#ifndef YY_NEVER_INTERACTIVE
+extern int isatty YY_PROTO(( int ));
+#endif
+#endif
+
+#ifdef YY_USE_PROTOS
+void yy_init_buffer( YY_BUFFER_STATE b, FILE *file )
+#else
+void yy_init_buffer( b, file )
+YY_BUFFER_STATE b;
+FILE *file;
+#endif
+
+
+       {
+       yy_flush_buffer( b );
+
+       b->yy_input_file = file;
+       b->yy_fill_buffer = 1;
+
+#if YY_ALWAYS_INTERACTIVE
+       b->yy_is_interactive = 1;
+#else
+#if YY_NEVER_INTERACTIVE
+       b->yy_is_interactive = 0;
+#else
+       b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+#endif
+#endif
+       }
+
+
+#ifdef YY_USE_PROTOS
+void yy_flush_buffer( YY_BUFFER_STATE b )
+#else
+void yy_flush_buffer( b )
+YY_BUFFER_STATE b;
+#endif
+
+       {
+       if ( ! b )
+               return;
+
+       b->yy_n_chars = 0;
+
+       /* We always need two end-of-buffer characters.  The first causes
+        * a transition to the end-of-buffer state.  The second causes
+        * a jam in that state.
+        */
+       b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+       b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+       b->yy_buf_pos = &b->yy_ch_buf[0];
+
+       b->yy_at_bol = 1;
+       b->yy_buffer_status = YY_BUFFER_NEW;
+
+       if ( b == yy_current_buffer )
+               yy_load_buffer_state();
+       }
+
+
+#ifndef YY_NO_SCAN_BUFFER
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_scan_buffer( char *base, yy_size_t size )
+#else
+YY_BUFFER_STATE yy_scan_buffer( base, size )
+char *base;
+yy_size_t size;
+#endif
+       {
+       YY_BUFFER_STATE b;
+
+       if ( size < 2 ||
+            base[size-2] != YY_END_OF_BUFFER_CHAR ||
+            base[size-1] != YY_END_OF_BUFFER_CHAR )
+               /* They forgot to leave room for the EOB's. */
+               return 0;
+
+       b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) );
+       if ( ! b )
+               YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" );
+
+       b->yy_buf_size = size - 2;      /* "- 2" to take care of EOB's */
+       b->yy_buf_pos = b->yy_ch_buf = base;
+       b->yy_is_our_buffer = 0;
+       b->yy_input_file = 0;
+       b->yy_n_chars = b->yy_buf_size;
+       b->yy_is_interactive = 0;
+       b->yy_at_bol = 1;
+       b->yy_fill_buffer = 0;
+       b->yy_buffer_status = YY_BUFFER_NEW;
+
+       yy_switch_to_buffer( b );
+
+       return b;
+       }
+#endif
+
+
+#ifndef YY_NO_SCAN_STRING
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_scan_string( yyconst char *yy_str )
+#else
+YY_BUFFER_STATE yy_scan_string( yy_str )
+yyconst char *yy_str;
+#endif
+       {
+       int len;
+       for ( len = 0; yy_str[len]; ++len )
+               ;
+
+       return yy_scan_bytes( yy_str, len );
+       }
+#endif
+
+
+#ifndef YY_NO_SCAN_BYTES
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_scan_bytes( yyconst char *bytes, int len )
+#else
+YY_BUFFER_STATE yy_scan_bytes( bytes, len )
+yyconst char *bytes;
+int len;
+#endif
+       {
+       YY_BUFFER_STATE b;
+       char *buf;
+       yy_size_t n;
+       int i;
+
+       /* Get memory for full buffer, including space for trailing EOB's. */
+       n = len + 2;
+       buf = (char *) yy_flex_alloc( n );
+       if ( ! buf )
+               YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" );
+
+       for ( i = 0; i < len; ++i )
+               buf[i] = bytes[i];
+
+       buf[len] = buf[len+1] = YY_END_OF_BUFFER_CHAR;
+
+       b = yy_scan_buffer( buf, n );
+       if ( ! b )
+               YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" );
+
+       /* It's okay to grow etc. this buffer, and we should throw it
+        * away when we're done.
+        */
+       b->yy_is_our_buffer = 1;
+
+       return b;
+       }
+#endif
+
+
+#ifndef YY_NO_PUSH_STATE
+#ifdef YY_USE_PROTOS
+static void yy_push_state( int new_state )
+#else
+static void yy_push_state( new_state )
+int new_state;
+#endif
+       {
+       if ( yy_start_stack_ptr >= yy_start_stack_depth )
+               {
+               yy_size_t new_size;
+
+               yy_start_stack_depth += YY_START_STACK_INCR;
+               new_size = yy_start_stack_depth * sizeof( int );
+
+               if ( ! yy_start_stack )
+                       yy_start_stack = (int *) yy_flex_alloc( new_size );
+
+               else
+                       yy_start_stack = (int *) yy_flex_realloc(
+                                       (void *) yy_start_stack, new_size );
+
+               if ( ! yy_start_stack )
+                       YY_FATAL_ERROR(
+                       "out of memory expanding start-condition stack" );
+               }
+
+       yy_start_stack[yy_start_stack_ptr++] = YY_START;
+
+       BEGIN(new_state);
+       }
+#endif
+
+
+#ifndef YY_NO_POP_STATE
+static void yy_pop_state()
+       {
+       if ( --yy_start_stack_ptr < 0 )
+               YY_FATAL_ERROR( "start-condition stack underflow" );
+
+       BEGIN(yy_start_stack[yy_start_stack_ptr]);
+       }
+#endif
+
+
+#ifndef YY_NO_TOP_STATE
+static int yy_top_state()
+       {
+       return yy_start_stack[yy_start_stack_ptr - 1];
+       }
+#endif
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+#ifdef YY_USE_PROTOS
+static void yy_fatal_error( yyconst char msg[] )
+#else
+static void yy_fatal_error( msg )
+char msg[];
+#endif
+       {
+       (void) fprintf( stderr, "%s\n", msg );
+       exit( YY_EXIT_FAILURE );
+       }
+
+
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+       do \
+               { \
+               /* Undo effects of setting up yytext. */ \
+               yytext[yyleng] = yy_hold_char; \
+               yy_c_buf_p = yytext + n; \
+               yy_hold_char = *yy_c_buf_p; \
+               *yy_c_buf_p = '\0'; \
+               yyleng = n; \
+               } \
+       while ( 0 )
+
+
+/* Internal utility routines. */
+
+#ifndef yytext_ptr
+#ifdef YY_USE_PROTOS
+static void yy_flex_strncpy( char *s1, yyconst char *s2, int n )
+#else
+static void yy_flex_strncpy( s1, s2, n )
+char *s1;
+yyconst char *s2;
+int n;
+#endif
+       {
+       register int i;
+       for ( i = 0; i < n; ++i )
+               s1[i] = s2[i];
+       }
+#endif
+
+#ifdef YY_NEED_STRLEN
+#ifdef YY_USE_PROTOS
+static int yy_flex_strlen( yyconst char *s )
+#else
+static int yy_flex_strlen( s )
+yyconst char *s;
+#endif
+       {
+       register int n;
+       for ( n = 0; s[n]; ++n )
+               ;
+
+       return n;
+       }
+#endif
+
+
+#ifdef YY_USE_PROTOS
+static void *yy_flex_alloc( yy_size_t size )
+#else
+static void *yy_flex_alloc( size )
+yy_size_t size;
+#endif
+       {
+       return (void *) malloc( size );
+       }
+
+#ifdef YY_USE_PROTOS
+static void *yy_flex_realloc( void *ptr, yy_size_t size )
+#else
+static void *yy_flex_realloc( ptr, size )
+void *ptr;
+yy_size_t size;
+#endif
+       {
+       /* The cast to (char *) in the following accommodates both
+        * implementations that use char* generic pointers, and those
+        * that use void* generic pointers.  It works with the latter
+        * because both ANSI C and C++ allow castless assignment from
+        * any pointer type to void*, and deal with argument conversions
+        * as though doing an assignment.
+        */
+       return (void *) realloc( (char *) ptr, size );
+       }
+
+#ifdef YY_USE_PROTOS
+static void yy_flex_free( void *ptr )
+#else
+static void yy_flex_free( ptr )
+void *ptr;
+#endif
+       {
+       free( ptr );
+       }
+
+#if YY_MAIN
+int main()
+       {
+       yylex();
+       return 0;
+       }
+#endif
+#line 247 "ssl_expr_scan.l"
+
+
+int yyinput(char *buf, int max_size)
+{
+    int n;
+
+    if ((n = MIN(max_size, ssl_expr_info.inputbuf
+                         + ssl_expr_info.inputlen 
+                         - ssl_expr_info.inputptr)) <= 0)
+        return YY_NULL;
+    memcpy(buf, ssl_expr_info.inputptr, n);
+    ssl_expr_info.inputptr += n;
+    return n;
+}
+
diff --git a/modules/ssl/ssl_expr_scan.l b/modules/ssl/ssl_expr_scan.l
new file mode 100644 (file)
index 0000000..a0db7cc
--- /dev/null
@@ -0,0 +1,261 @@
+/*                      _             _ 
+**  _ __ ___   ___   __| |    ___ ___| |  
+** | '_ ` _ \ / _ \ / _` |   / __/ __| |  
+** | | | | | | (_) | (_| |   \__ \__ \ | mod_ssl - Apache Interface to OpenSSL
+** |_| |_| |_|\___/ \__,_|___|___/___/_| http://www.modssl.org/
+**                      |_____|         
+**  ssl_expr_scan.l
+**  Expression Scanner
+*/
+
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by 
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ *    nor may "mod_ssl" appear in their names without prior
+ *    written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by 
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+                             /* ``Killing for peace is 
+                                  like fucking for virginity.''
+                                             -- Unknown  */
+
+/*  _________________________________________________________________
+**
+**  Expression Scanner
+**  _________________________________________________________________
+*/
+
+%{
+#include "mod_ssl.h"
+
+#include "ssl_expr_parse.h"
+
+#define YY_NO_UNPUT 1
+int yyinput(char *buf, int max_size);
+
+#undef  YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+    (result = yyinput(buf, max_size))
+
+#define MAX_STR_LEN 2048
+%}
+
+%pointer
+/* %option stack */
+%option never-interactive
+%option noyywrap
+%x str
+%x regex regex_flags
+
+%%
+  
+  char  caStr[MAX_STR_LEN];
+  char *cpStr = NULL;
+  char  caRegex[MAX_STR_LEN];
+  char *cpRegex = NULL;
+  char  cRegexDel = NUL;
+
+ /*
+  * Whitespaces
+  */
+[ \t\n]+ { 
+    /* NOP */
+}
+
+ /*
+  * C-style strings ("...")
+  */
+\" {
+    cpStr = caStr;
+    BEGIN(str);
+}
+<str>\" {
+    BEGIN(INITIAL);
+    *cpStr = NUL;
+    yylval.cpVal = ap_pstrdup(ssl_expr_info.pool, caStr);
+    return T_STRING;
+}
+<str>\n {
+    yyerror("Unterminated string");
+}
+<str>\\[0-7]{1,3} {
+    int result;
+
+    (void)sscanf(yytext+1, "%o", &result);
+    if (result > 0xff)
+        yyerror("Escape sequence out of bound");
+    else
+        *cpStr++ = result;
+}
+<str>\\[0-9]+ {
+    yyerror("Bad escape sequence");
+}
+<str>\\n { *cpStr++ = '\n'; }
+<str>\\r { *cpStr++ = '\r'; }
+<str>\\t { *cpStr++ = '\t'; }
+<str>\\b { *cpStr++ = '\b'; }
+<str>\\f { *cpStr++ = '\f'; }
+<str>\\(.|\n) {
+    *cpStr++ = yytext[1];
+}
+<str>[^\\\n\"]+ {
+    char *cp = yytext;
+    while (*cp != NUL)
+        *cpStr++ = *cp++;
+}
+<str>. {
+    *cpStr++ = yytext[1];
+}
+
+ /*
+  * Regular Expression
+  */
+"m". {
+    cRegexDel = yytext[1];
+    cpRegex = caRegex;
+    BEGIN(regex);
+}
+<regex>.|\n {
+    if (yytext[0] == cRegexDel) {
+        *cpRegex = NUL;
+        BEGIN(regex_flags);
+    }
+    else {
+        *cpRegex++ = yytext[0];
+    }
+}
+<regex_flags>i {
+    yylval.cpVal = ap_pstrdup(ssl_expr_info.pool, caRegex);
+    BEGIN(INITIAL);
+    return T_REGEX_I;
+}
+<regex_flags>.|\n {
+    yylval.cpVal = ap_pstrdup(ssl_expr_info.pool, caRegex);
+    yyless(0);
+    BEGIN(INITIAL);
+    return T_REGEX;
+}
+<regex_flags><<EOF>> {
+    yylval.cpVal = ap_pstrdup(ssl_expr_info.pool, caRegex);
+    BEGIN(INITIAL);
+    return T_REGEX;
+}
+
+ /*
+  * Operators
+  */
+"eq"  { return T_OP_EQ; }
+"=="  { return T_OP_EQ; }
+"ne"  { return T_OP_NE; }
+"!="  { return T_OP_NE; }
+"lt"  { return T_OP_LT; }
+"<"   { return T_OP_LT; }
+"le"  { return T_OP_LE; }
+"<="  { return T_OP_LE; }
+"gt"  { return T_OP_GT; }
+">"   { return T_OP_GT; }
+"ge"  { return T_OP_GE; }
+">="  { return T_OP_GE; }
+"=~"  { return T_OP_REG; }
+"!~"  { return T_OP_NRE; }
+"and" { return T_OP_AND; }
+"&&"  { return T_OP_AND; }
+"or"  { return T_OP_OR; }
+"||"  { return T_OP_OR; }
+"not" { return T_OP_NOT; }
+"!"   { return T_OP_NOT; }
+"in"  { return T_OP_IN; }
+
+ /*
+  * Functions
+  */
+"file" { return T_FUNC_FILE; }
+
+ /*
+  * Specials
+  */
+"true"  { return T_TRUE; }
+"false" { return T_FALSE; }
+
+ /*
+  * Digits
+  */
+[0-9]+ {
+    yylval.cpVal = ap_pstrdup(ssl_expr_info.pool, yytext);
+    return T_DIGIT;
+}
+
+ /*
+  * Identifiers
+  */
+[a-zA-Z][a-zA-Z0-9_:-]* {
+    yylval.cpVal = ap_pstrdup(ssl_expr_info.pool, yytext);
+    return T_ID;
+}
+
+ /*
+  * Anything else is returned as is...
+  */
+.|\n { 
+    return yytext[0];
+}
+
+%%
+
+int yyinput(char *buf, int max_size)
+{
+    int n;
+
+    if ((n = MIN(max_size, ssl_expr_info.inputbuf
+                         + ssl_expr_info.inputlen 
+                         - ssl_expr_info.inputptr)) <= 0)
+        return YY_NULL;
+    memcpy(buf, ssl_expr_info.inputptr, n);
+    ssl_expr_info.inputptr += n;
+    return n;
+}
+
diff --git a/modules/ssl/ssl_scache.c b/modules/ssl/ssl_scache.c
new file mode 100644 (file)
index 0000000..139c786
--- /dev/null
@@ -0,0 +1,204 @@
+/*                      _             _
+**  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
+** | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
+** | | | | | | (_) | (_| |   \__ \__ \ |  www.modssl.org
+** |_| |_| |_|\___/ \__,_|___|___/___/_|  ftp.modssl.org
+**                      |_____|
+**  ssl_scache.c
+**  Session Cache Abstraction
+*/
+
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ *    nor may "mod_ssl" appear in their names without prior
+ *    written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+                             /* ``Open-Source Software: generous
+                                  programmers from around the world all
+                                  join forces to help you shoot
+                                  yourself in the foot for free.''
+                                                 -- Unknown         */
+#include "mod_ssl.h"
+
+/*  _________________________________________________________________
+**
+**  Session Cache: Common Abstraction Layer
+**  _________________________________________________________________
+*/
+
+void ssl_scache_init(server_rec *s, pool *p)
+{
+    SSLModConfigRec *mc = myModConfig();
+
+    if (mc->nSessionCacheMode == SSL_SCMODE_DBM)
+        ssl_scache_dbm_init(s, p);
+    else if (mc->nSessionCacheMode == SSL_SCMODE_SHMHT)
+        ssl_scache_shmht_init(s, p);
+    else if (mc->nSessionCacheMode == SSL_SCMODE_SHMCB)
+        ssl_scache_shmcb_init(s, p);
+#ifdef SSL_VENDOR
+    else
+        ap_hook_use("ap::mod_ssl::vendor::scache_init",
+                    AP_HOOK_SIG3(void,ptr,ptr), AP_HOOK_ALL, s, p);
+#endif
+    return;
+}
+
+void ssl_scache_kill(server_rec *s)
+{
+    SSLModConfigRec *mc = myModConfig();
+
+    if (mc->nSessionCacheMode == SSL_SCMODE_DBM)
+        ssl_scache_dbm_kill(s);
+    else if (mc->nSessionCacheMode == SSL_SCMODE_SHMHT)
+        ssl_scache_shmht_kill(s);
+    else if (mc->nSessionCacheMode == SSL_SCMODE_SHMCB)
+        ssl_scache_shmcb_kill(s);
+#ifdef SSL_VENDOR
+    else
+        ap_hook_use("ap::mod_ssl::vendor::scache_kill",
+                    AP_HOOK_SIG2(void,ptr), AP_HOOK_ALL, s);
+#endif
+    return;
+}
+
+BOOL ssl_scache_store(server_rec *s, UCHAR *id, int idlen, time_t expiry, SSL_SESSION *sess)
+{
+    SSLModConfigRec *mc = myModConfig();
+    BOOL rv = FALSE;
+
+    if (mc->nSessionCacheMode == SSL_SCMODE_DBM)
+        rv = ssl_scache_dbm_store(s, id, idlen, expiry, sess);
+    else if (mc->nSessionCacheMode == SSL_SCMODE_SHMHT)
+        rv = ssl_scache_shmht_store(s, id, idlen, expiry, sess);
+    else if (mc->nSessionCacheMode == SSL_SCMODE_SHMCB)
+        rv = ssl_scache_shmcb_store(s, id, idlen, expiry, sess);
+#ifdef SSL_VENDOR
+    else
+        ap_hook_use("ap::mod_ssl::vendor::scache_store",
+                    AP_HOOK_SIG6(int,ptr,ptr,int,int,ptr), AP_HOOK_ALL,
+                    (int *)&rv, s, id, idlen, (int)expiry, sess);
+#endif
+    return rv;
+}
+
+SSL_SESSION *ssl_scache_retrieve(server_rec *s, UCHAR *id, int idlen)
+{
+    SSLModConfigRec *mc = myModConfig();
+    SSL_SESSION *sess = NULL;
+
+    if (mc->nSessionCacheMode == SSL_SCMODE_DBM)
+        sess = ssl_scache_dbm_retrieve(s, id, idlen);
+    else if (mc->nSessionCacheMode == SSL_SCMODE_SHMHT)
+        sess = ssl_scache_shmht_retrieve(s, id, idlen);
+    else if (mc->nSessionCacheMode == SSL_SCMODE_SHMCB)
+        sess = ssl_scache_shmcb_retrieve(s, id, idlen);
+#ifdef SSL_VENDOR
+    else
+        ap_hook_use("ap::mod_ssl::vendor::scache_retrieve",
+                    AP_HOOK_SIG4(ptr,ptr,ptr,int), AP_HOOK_ALL, 
+                    &sess, s, id, idlen);
+#endif
+    return sess;
+}
+
+void ssl_scache_remove(server_rec *s, UCHAR *id, int idlen)
+{
+    SSLModConfigRec *mc = myModConfig();
+
+    if (mc->nSessionCacheMode == SSL_SCMODE_DBM)
+        ssl_scache_dbm_remove(s, id, idlen);
+    else if (mc->nSessionCacheMode == SSL_SCMODE_SHMHT)
+        ssl_scache_shmht_remove(s, id, idlen);
+    else if (mc->nSessionCacheMode == SSL_SCMODE_SHMCB)
+        ssl_scache_shmcb_remove(s, id, idlen);
+#ifdef SSL_VENDOR
+    else
+        ap_hook_use("ap::mod_ssl::vendor::scache_remove",
+                    AP_HOOK_SIG4(void,ptr,ptr,int), AP_HOOK_ALL, s, id, idlen);
+#endif
+    return;
+}
+
+void ssl_scache_status(server_rec *s, pool *p, void (*func)(char *, void *), void *arg)
+{
+    SSLModConfigRec *mc = myModConfig();
+
+    if (mc->nSessionCacheMode == SSL_SCMODE_DBM)
+        ssl_scache_dbm_status(s, p, func, arg);
+    else if (mc->nSessionCacheMode == SSL_SCMODE_SHMHT)
+        ssl_scache_shmht_status(s, p, func, arg);
+    else if (mc->nSessionCacheMode == SSL_SCMODE_SHMCB)
+        ssl_scache_shmcb_status(s, p, func, arg);
+#ifdef SSL_VENDOR
+    else
+        ap_hook_use("ap::mod_ssl::vendor::scache_status",
+                    AP_HOOK_SIG5(void,ptr,ptr,ptr,ptr), AP_HOOK_ALL, 
+                    s, p, func, arg);
+#endif
+    return;
+}
+
+void ssl_scache_expire(server_rec *s)
+{
+    SSLModConfigRec *mc = myModConfig();
+
+    if (mc->nSessionCacheMode == SSL_SCMODE_DBM)
+        ssl_scache_dbm_expire(s);
+    else if (mc->nSessionCacheMode == SSL_SCMODE_SHMHT)
+        ssl_scache_shmht_expire(s);
+    else if (mc->nSessionCacheMode == SSL_SCMODE_SHMCB)
+        ssl_scache_shmcb_expire(s);
+#ifdef SSL_VENDOR
+    else
+        ap_hook_use("ap::mod_ssl::vendor::scache_expire",
+                    AP_HOOK_SIG2(void,ptr), AP_HOOK_ALL, s);
+#endif
+    return;
+}
+
diff --git a/modules/ssl/ssl_scache_dbm.c b/modules/ssl/ssl_scache_dbm.c
new file mode 100644 (file)
index 0000000..323c612
--- /dev/null
@@ -0,0 +1,440 @@
+/*                      _             _
+**  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
+** | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
+** | | | | | | (_) | (_| |   \__ \__ \ |  www.modssl.org
+** |_| |_| |_|\___/ \__,_|___|___/___/_|  ftp.modssl.org
+**                      |_____|
+**  ssl_scache_dbm.c
+**  Session Cache via DBM
+*/
+
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ *    nor may "mod_ssl" appear in their names without prior
+ *    written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include "mod_ssl.h"
+
+void ssl_scache_dbm_init(server_rec *s, pool *p)
+{
+    SSLModConfigRec *mc = myModConfig();
+    DBM *dbm;
+
+    /* for the DBM we need the data file */
+    if (mc->szSessionCacheDataFile == NULL) {
+        ssl_log(s, SSL_LOG_ERROR, "SSLSessionCache required");
+        ssl_die();
+    }
+
+    /* open it once to create it and to make sure it _can_ be created */
+    ssl_mutex_on(s);
+    if ((dbm = ssl_dbm_open(mc->szSessionCacheDataFile,
+                            O_RDWR|O_CREAT, SSL_DBM_FILE_MODE)) == NULL) {
+        ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO,
+                "Cannot create SSLSessionCache DBM file `%s'",
+                mc->szSessionCacheDataFile);
+        ssl_mutex_off(s);
+        return;
+    }
+    ssl_dbm_close(dbm);
+
+#if !defined(OS2) && !defined(WIN32)
+    /*
+     * We have to make sure the Apache child processes have access to
+     * the DBM file. But because there are brain-dead platforms where we
+     * cannot exactly determine the suffixes we try all possibilities.
+     */
+    if (geteuid() == 0 /* is superuser */) {
+        chown(mc->szSessionCacheDataFile, ap_user_id, -1 /* no gid change */);
+        if (chown(ap_pstrcat(p, mc->szSessionCacheDataFile, SSL_DBM_FILE_SUFFIX_DIR, NULL),
+                  ap_user_id, -1) == -1) {
+            if (chown(ap_pstrcat(p, mc->szSessionCacheDataFile, ".db", NULL),
+                      ap_user_id, -1) == -1)
+                chown(ap_pstrcat(p, mc->szSessionCacheDataFile, ".dir", NULL),
+                      ap_user_id, -1);
+        }
+        if (chown(ap_pstrcat(p, mc->szSessionCacheDataFile, SSL_DBM_FILE_SUFFIX_PAG, NULL),
+                  ap_user_id, -1) == -1) {
+            if (chown(ap_pstrcat(p, mc->szSessionCacheDataFile, ".db", NULL),
+                      ap_user_id, -1) == -1)
+                chown(ap_pstrcat(p, mc->szSessionCacheDataFile, ".pag", NULL),
+                      ap_user_id, -1);
+        }
+    }
+#endif
+    ssl_mutex_off(s);
+    ssl_scache_dbm_expire(s);
+    return;
+}
+
+void ssl_scache_dbm_kill(server_rec *s)
+{
+    SSLModConfigRec *mc = myModConfig();
+    pool *p;
+
+    if ((p = ap_make_sub_pool(NULL)) != NULL) {
+        /* the correct way */
+        unlink(ap_pstrcat(p, mc->szSessionCacheDataFile, SSL_DBM_FILE_SUFFIX_DIR, NULL));
+        unlink(ap_pstrcat(p, mc->szSessionCacheDataFile, SSL_DBM_FILE_SUFFIX_PAG, NULL));
+        /* the additional ways to be sure */
+        unlink(ap_pstrcat(p, mc->szSessionCacheDataFile, ".dir", NULL));
+        unlink(ap_pstrcat(p, mc->szSessionCacheDataFile, ".pag", NULL));
+        unlink(ap_pstrcat(p, mc->szSessionCacheDataFile, ".db", NULL));
+        unlink(mc->szSessionCacheDataFile);
+        ap_destroy_pool(p);
+    }
+    return;
+}
+
+BOOL ssl_scache_dbm_store(server_rec *s, UCHAR *id, int idlen, time_t expiry, SSL_SESSION *sess)
+{
+    SSLModConfigRec *mc = myModConfig();
+    DBM *dbm;
+    datum dbmkey;
+    datum dbmval;
+    UCHAR ucaData[SSL_SESSION_MAX_DER];
+    int nData;
+    UCHAR *ucp;
+
+    /* streamline session data */
+    ucp = ucaData;
+    nData = i2d_SSL_SESSION(sess, &ucp);
+
+    /* be careful: do not try to store too much bytes in a DBM file! */
+#ifdef SSL_USE_SDBM
+    if ((idlen + nData) >= PAIRMAX)
+        return FALSE;
+#else
+    if ((idlen + nData) >= 950 /* at least less than approx. 1KB */)
+        return FALSE;
+#endif
+
+    /* create DBM key */
+    dbmkey.dptr  = (char *)id;
+    dbmkey.dsize = idlen;
+
+    /* create DBM value */
+    dbmval.dsize = sizeof(time_t) + nData;
+    dbmval.dptr  = (char *)malloc(dbmval.dsize);
+    if (dbmval.dptr == NULL)
+        return FALSE;
+    memcpy((char *)dbmval.dptr, &expiry, sizeof(time_t));
+    memcpy((char *)dbmval.dptr+sizeof(time_t), ucaData, nData);
+
+    /* and store it to the DBM file */
+    ssl_mutex_on(s);
+    if ((dbm = ssl_dbm_open(mc->szSessionCacheDataFile,
+                            O_RDWR, SSL_DBM_FILE_MODE)) == NULL) {
+        ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO,
+                "Cannot open SSLSessionCache DBM file `%s' for writing (store)",
+                mc->szSessionCacheDataFile);
+        ssl_mutex_off(s);
+        free(dbmval.dptr);
+        return FALSE;
+    }
+    if (ssl_dbm_store(dbm, dbmkey, dbmval, DBM_INSERT) < 0) {
+        ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO,
+                "Cannot store SSL session to DBM file `%s'",
+                mc->szSessionCacheDataFile);
+        ssl_dbm_close(dbm);
+        ssl_mutex_off(s);
+        free(dbmval.dptr);
+        return FALSE;
+    }
+    ssl_dbm_close(dbm);
+    ssl_mutex_off(s);
+
+    /* free temporary buffers */
+    free(dbmval.dptr);
+
+    /* allow the regular expiring to occur */
+    ssl_scache_dbm_expire(s);
+
+    return TRUE;
+}
+
+SSL_SESSION *ssl_scache_dbm_retrieve(server_rec *s, UCHAR *id, int idlen)
+{
+    SSLModConfigRec *mc = myModConfig();
+    DBM *dbm;
+    datum dbmkey;
+    datum dbmval;
+    SSL_SESSION *sess = NULL;
+    UCHAR *ucpData;
+    int nData;
+    time_t expiry;
+    time_t now;
+
+    /* allow the regular expiring to occur */
+    ssl_scache_dbm_expire(s);
+
+    /* create DBM key and values */
+    dbmkey.dptr  = (char *)id;
+    dbmkey.dsize = idlen;
+
+    /* and fetch it from the DBM file */
+    ssl_mutex_on(s);
+    if ((dbm = ssl_dbm_open(mc->szSessionCacheDataFile,
+                            O_RDONLY, SSL_DBM_FILE_MODE)) == NULL) {
+        ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO,
+                "Cannot open SSLSessionCache DBM file `%s' for reading (fetch)",
+                mc->szSessionCacheDataFile);
+        ssl_mutex_off(s);
+        return NULL;
+    }
+    dbmval = ssl_dbm_fetch(dbm, dbmkey);
+    ssl_dbm_close(dbm);
+    ssl_mutex_off(s);
+
+    /* immediately return if not found */
+    if (dbmval.dptr == NULL || dbmval.dsize <= sizeof(time_t))
+        return NULL;
+
+    /* parse resulting data */
+    nData = dbmval.dsize-sizeof(time_t);
+    ucpData = (UCHAR *)malloc(nData);
+    if (ucpData == NULL) 
+        return NULL;
+    memcpy(ucpData, (char *)dbmval.dptr+sizeof(time_t), nData);
+    memcpy(&expiry, dbmval.dptr, sizeof(time_t));
+
+    /* make sure the stuff is still not expired */
+    now = time(NULL);
+    if (expiry <= now) {
+        ssl_scache_dbm_remove(s, id, idlen);
+        return NULL;
+    }
+
+    /* unstreamed SSL_SESSION */
+    sess = d2i_SSL_SESSION(NULL, &ucpData, nData);
+
+    return sess;
+}
+
+void ssl_scache_dbm_remove(server_rec *s, UCHAR *id, int idlen)
+{
+    SSLModConfigRec *mc = myModConfig();
+    DBM *dbm;
+    datum dbmkey;
+
+    /* create DBM key and values */
+    dbmkey.dptr  = (char *)id;
+    dbmkey.dsize = idlen;
+
+    /* and delete it from the DBM file */
+    ssl_mutex_on(s);
+    if ((dbm = ssl_dbm_open(mc->szSessionCacheDataFile,
+                            O_RDWR, SSL_DBM_FILE_MODE)) == NULL) {
+        ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO,
+                "Cannot open SSLSessionCache DBM file `%s' for writing (delete)",
+                mc->szSessionCacheDataFile);
+        ssl_mutex_off(s);
+        return;
+    }
+    ssl_dbm_delete(dbm, dbmkey);
+    ssl_dbm_close(dbm);
+    ssl_mutex_off(s);
+
+    return;
+}
+
+void ssl_scache_dbm_expire(server_rec *s)
+{
+    SSLModConfigRec *mc = myModConfig();
+    SSLSrvConfigRec *sc = mySrvConfig(s);
+    static time_t tLast = 0;
+    DBM *dbm;
+    datum dbmkey;
+    datum dbmval;
+    pool *p;
+    time_t tExpiresAt;
+    int nElements = 0;
+    int nDeleted = 0;
+    int bDelete;
+    datum *keylist;
+    int keyidx;
+    int i;
+    time_t tNow;
+
+    /*
+     * make sure the expiration for still not-accessed session
+     * cache entries is done only from time to time
+     */
+    tNow = time(NULL);
+    if (tNow < tLast+sc->nSessionCacheTimeout)
+        return;
+    tLast = tNow;
+
+    /*
+     * Here we have to be very carefully: Not all DBM libraries are
+     * smart enough to allow one to iterate over the elements and at the
+     * same time delete expired ones. Some of them get totally crazy
+     * while others have no problems. So we have to do it the slower but
+     * more safe way: we first iterate over all elements and remember
+     * those which have to be expired. Then in a second pass we delete
+     * all those expired elements. Additionally we reopen the DBM file
+     * to be really safe in state.
+     */
+
+#define KEYMAX 1024
+
+    ssl_mutex_on(s);
+    for (;;) {
+        /* allocate the key array in a memory sub pool */
+        if ((p = ap_make_sub_pool(NULL)) == NULL)
+            break;
+        if ((keylist = ap_palloc(p, sizeof(dbmkey)*KEYMAX)) == NULL) {
+            ap_destroy_pool(p);
+            break;
+        }
+
+        /* pass 1: scan DBM database */
+        keyidx = 0;
+        if ((dbm = ssl_dbm_open(mc->szSessionCacheDataFile,
+                                O_RDWR, SSL_DBM_FILE_MODE)) == NULL) {
+            ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO,
+                    "Cannot open SSLSessionCache DBM file `%s' for scanning",
+                    mc->szSessionCacheDataFile);
+            ap_destroy_pool(p);
+            break;
+        }
+        dbmkey = ssl_dbm_firstkey(dbm);
+        while (dbmkey.dptr != NULL) {
+            nElements++;
+            bDelete = FALSE;
+            dbmval = ssl_dbm_fetch(dbm, dbmkey);
+            if (dbmval.dsize <= sizeof(time_t) || dbmval.dptr == NULL)
+                bDelete = TRUE;
+            else {
+                memcpy(&tExpiresAt, dbmval.dptr, sizeof(time_t));
+                if (tExpiresAt <= tNow)
+                    bDelete = TRUE;
+            }
+            if (bDelete) {
+                if ((keylist[keyidx].dptr = ap_palloc(p, dbmkey.dsize)) != NULL) {
+                    memcpy(keylist[keyidx].dptr, dbmkey.dptr, dbmkey.dsize);
+                    keylist[keyidx].dsize = dbmkey.dsize;
+                    keyidx++;
+                    if (keyidx == KEYMAX)
+                        break;
+                }
+            }
+            dbmkey = ssl_dbm_nextkey(dbm);
+        }
+        ssl_dbm_close(dbm);
+
+        /* pass 2: delete expired elements */
+        if ((dbm = ssl_dbm_open(mc->szSessionCacheDataFile,
+                                O_RDWR, SSL_DBM_FILE_MODE)) == NULL) {
+            ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO,
+                    "Cannot re-open SSLSessionCache DBM file `%s' for expiring",
+                    mc->szSessionCacheDataFile);
+            ap_destroy_pool(p);
+            break;
+        }
+        for (i = 0; i < keyidx; i++) {
+            ssl_dbm_delete(dbm, keylist[i]);
+            nDeleted++;
+        }
+        ssl_dbm_close(dbm);
+
+        /* destroy temporary pool */
+        ap_destroy_pool(p);
+
+        if (keyidx < KEYMAX)
+            break;
+    }
+    ssl_mutex_off(s);
+
+    ssl_log(s, SSL_LOG_TRACE, "Inter-Process Session Cache (DBM) Expiry: "
+            "old: %d, new: %d, removed: %d", nElements, nElements-nDeleted, nDeleted);
+    return;
+}
+
+void ssl_scache_dbm_status(server_rec *s, pool *p, void (*func)(char *, void *), void *arg)
+{
+    SSLModConfigRec *mc = myModConfig();
+    DBM *dbm;
+    datum dbmkey;
+    datum dbmval;
+    int nElem;
+    int nSize;
+    int nAverage;
+
+    nElem = 0;
+    nSize = 0;
+    ssl_mutex_on(s);
+    if ((dbm = ssl_dbm_open(mc->szSessionCacheDataFile,
+                            O_RDONLY, SSL_DBM_FILE_MODE)) == NULL) {
+        ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO,
+                "Cannot open SSLSessionCache DBM file `%s' for status retrival",
+                mc->szSessionCacheDataFile);
+        ssl_mutex_off(s);
+        return;
+    }
+    dbmkey = ssl_dbm_firstkey(dbm);
+    for ( ; dbmkey.dptr != NULL; dbmkey = ssl_dbm_nextkey(dbm)) {
+        dbmval = ssl_dbm_fetch(dbm, dbmkey);
+        if (dbmval.dptr == NULL)
+            continue;
+        nElem += 1;
+        nSize += dbmval.dsize;
+    }
+    ssl_dbm_close(dbm);
+    ssl_mutex_off(s);
+    if (nSize > 0 && nElem > 0)
+        nAverage = nSize / nElem;
+    else
+        nAverage = 0;
+    func(ap_psprintf(p, "cache type: <b>DBM</b>, maximum size: <b>unlimited</b><br>"), arg);
+    func(ap_psprintf(p, "current sessions: <b>%d</b>, current size: <b>%d</b> bytes<br>", nElem, nSize), arg);
+    func(ap_psprintf(p, "average session size: <b>%d</b> bytes<br>", nAverage), arg);
+    return;
+}
+
diff --git a/modules/ssl/ssl_scache_shmcb.c b/modules/ssl/ssl_scache_shmcb.c
new file mode 100644 (file)
index 0000000..e588f0a
--- /dev/null
@@ -0,0 +1,1343 @@
+/*                      _             _
+**  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
+** | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
+** | | | | | | (_) | (_| |   \__ \__ \ |  www.modssl.org
+** |_| |_| |_|\___/ \__,_|___|___/___/_|  ftp.modssl.org
+**                      |_____|
+**  ssl_scache_shmcb.c
+**  Session Cache via Shared Memory (Cyclic Buffer Variant)
+*/
+
+/* ====================================================================
+ * Copyright (c) 2000-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ *    nor may "mod_ssl" appear in their names without prior
+ *    written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include "mod_ssl.h"
+
+/* 
+ * This shared memory based SSL session cache implementation was
+ * originally written by Geoff Thorpe <geoff@eu.c2.net> for C2Net Europe
+ * and as a contribution to Ralf Engelschall's mod_ssl project.
+ */
+
+/*
+ * The shared-memory segment header can be cast to and from the
+ * SHMCBHeader type, all other structures need to be initialised by
+ * utility functions.
+ *
+ * The "header" looks like this;
+ *
+ * data applying to the overall structure:
+ * - division_offset (unsigned int):
+ *   how far into the shared memory segment the first division is.
+ * - division_size (unsigned int):
+ *   how many bytes each division occupies.
+ *   (NB: This includes the queue and the cache)
+ * - division_mask (unsigned char):
+ *   the "mask" in the next line. Add one to this,
+ *   and that's the number of divisions.
+ *
+ * data applying to within each division:
+ * - queue_size (unsigned int):
+ *   how big each "queue" is. NB: The queue is the first block in each
+ *   division and is followed immediately by the cache itself so so
+ *   there's no cache_offset value.
+ *
+ * data applying to within each queue:
+ * - index_num (unsigned char):
+ *   how many indexes in each cache's queue
+ * - index_offset (unsigned char):
+ *   how far into the queue the first index is.
+ * - index_size:
+ *   how big each index is.
+ *
+ * data applying to within each cache:
+ * - cache_data_offset (unsigned int):
+ *   how far into the cache the session-data array is stored.
+ * - cache_data_size (unsigned int):
+ *   how big each cache's data block is.
+ *
+ * statistics data (this will eventually be per-division but right now
+ * there's only one mutex):
+ * - stores (unsigned long):
+ *   how many stores have been performed in the cache.
+ * - expiries (unsigned long):
+ *   how many session have been expired from the cache.
+ * - scrolled (unsigned long):
+ *   how many sessions have been scrolled out of full cache during a
+ *   "store" operation. This is different to the "removes" stats as
+ *   they are requested by mod_ssl/Apache, these are done because of
+ *   cache logistics. (NB: Also, this value should be deducible from
+ *   the others if my code has no bugs, but I count it anyway - plus
+ *   it helps debugging :-).
+ * - retrieves_hit (unsigned long):
+ *   how many session-retrieves have succeeded.
+ * - retrieves_miss (unsigned long):
+ *   how many session-retrieves have failed.
+ * - removes_hit (unsigned long):
+ * - removes_miss (unsigned long):
+ * 
+ * Following immediately after the header is an array of "divisions".
+ * Each division is simply a "queue" immediately followed by its
+ * corresponding "cache". Each division handles some pre-defined band
+ * of sessions by using the "division_mask" in the header. Eg. if
+ * division_mask=0x1f then there are 32 divisions, the first of which
+ * will store sessions whose least-significant 5 bits are 0, the second
+ * stores session whose LS 5 bits equal 1, etc. A queue is an indexing
+ * structure referring to its corresponding cache.
+ *
+ * A "queue" looks like this;
+ *
+ * - first_pos (unsigned int):
+ *   the location within the array of indexes where the virtual
+ *   "left-hand-edge" of the cyclic buffer is.
+ * - pos_count (unsigned int):
+ *   the number of indexes occupied from first_pos onwards.
+ *
+ * ...followed by an array of indexes, each of which can be
+ * memcpy'd to and from an SHMCBIndex, and look like this;
+ *
+ * - expires (time_t):
+ *   the time() value at which this session expires.
+ * - offset (unsigned int):
+ *   the offset within the cache data block where the corresponding
+ *   session is stored.
+ * - s_id2 (unsigned char):
+ *   the second byte of the session_id, stored as an optimisation to
+ *   reduce the number of d2i_SSL_SESSION calls that are made when doing
+ *   a lookup.
+ * - removed (unsigned char):
+ *   a byte used to indicate whether a session has been "passively"
+ *   removed. Ie. it is still in the cache but is to be disregarded by
+ *   any "retrieve" operation.
+ *
+ * A "cache" looks like this;
+ *
+ * - first_pos (unsigned int):
+ *   the location within the data block where the virtual
+ *   "left-hand-edge" of the cyclic buffer is.
+ * - pos_count (unsigned int):
+ *   the number of bytes used in the data block from first_pos onwards.
+ *
+ * ...followed by the data block in which actual DER-encoded SSL
+ * sessions are stored.
+ */
+
+/* 
+ * Header - can be memcpy'd to and from the front of the shared
+ * memory segment. NB: The first copy (commented out) has the
+ * elements in a meaningful order, but due to data-alignment
+ * braindeadness, the second (uncommented) copy has the types grouped
+ * so as to decrease "struct-bloat". sigh.
+ */
+typedef struct {
+#if 0
+    unsigned char division_mask;
+    unsigned int division_offset;
+    unsigned int division_size;
+    unsigned int queue_size;
+    unsigned char index_num;
+    unsigned char index_offset;
+    unsigned char index_size;
+    unsigned int cache_data_offset;
+    unsigned int cache_data_size;
+    unsigned long num_stores;
+    unsigned long num_expiries;
+    unsigned long num_scrolled;
+    unsigned long num_retrieves_hit;
+    unsigned long num_retrieves_miss;
+    unsigned long num_removes_hit;
+    unsigned long num_removes_miss;
+#else
+    unsigned long num_stores;
+    unsigned long num_expiries;
+    unsigned long num_scrolled;
+    unsigned long num_retrieves_hit;
+    unsigned long num_retrieves_miss;
+    unsigned long num_removes_hit;
+    unsigned long num_removes_miss;
+    unsigned int division_offset;
+    unsigned int division_size;
+    unsigned int queue_size;
+    unsigned int cache_data_offset;
+    unsigned int cache_data_size;
+    unsigned char division_mask;
+    unsigned char index_num;
+    unsigned char index_offset;
+    unsigned char index_size;
+#endif
+} SHMCBHeader;
+
+/* 
+ * Index - can be memcpy'd to and from an index inside each
+ * queue's index array.
+ */
+typedef struct {
+    time_t expires;
+    unsigned int offset;
+    unsigned char s_id2;
+    unsigned char removed;
+} SHMCBIndex;
+
+/* 
+ * Queue - must be populated by a call to shmcb_get_division
+ * and the structure's pointers are used for updating (ie.
+ * the structure doesn't need any "set" to update values).
+ */
+typedef struct {
+    SHMCBHeader *header;
+    unsigned int *first_pos;
+    unsigned int *pos_count;
+    SHMCBIndex *indexes;
+} SHMCBQueue;
+
+/* 
+ * Cache - same comment as for Queue. 'Queue's are in a 1-1
+ * correspondance with 'Cache's and are usually carried round
+ * in a pair, they are only seperated for clarity.
+ */
+typedef struct {
+    SHMCBHeader *header;
+    unsigned int *first_pos;
+    unsigned int *pos_count;
+    unsigned char *data;
+} SHMCBCache;
+
+/*
+ * Forward function prototypes.
+ */
+
+/* Functions for working around data-alignment-picky systems (sparcs,
+   Irix, etc). These use "memcpy" as a way of foxing these systems into
+   treating the composite types as byte-arrays rather than higher-level
+   primitives that it prefers to have 4-(or 8-)byte aligned. I don't
+   envisage this being a performance issue as a couple of 2 or 4 byte
+   memcpys can hardly make a dent on the massive memmove operations this
+   cache technique avoids, nor the overheads of ASN en/decoding. */
+static unsigned int shmcb_get_safe_uint(unsigned int *);
+static void shmcb_set_safe_uint(unsigned int *, unsigned int);
+#if 0 /* Unused so far */
+static unsigned long shmcb_get_safe_ulong(unsigned long *);
+static void shmcb_set_safe_ulong(unsigned long *, unsigned long);
+#endif
+static time_t shmcb_get_safe_time(time_t *);
+static void shmcb_set_safe_time(time_t *, time_t);
+
+/* Underlying functions for session-caching */
+static BOOL shmcb_init_memory(server_rec *, void *, unsigned int);
+static BOOL shmcb_store_session(server_rec *, void *, UCHAR *, int, SSL_SESSION *, time_t);
+static SSL_SESSION *shmcb_retrieve_session(server_rec *, void *, UCHAR *, int);
+static BOOL shmcb_remove_session(server_rec *, void *, UCHAR *, int);
+
+/* Utility functions for manipulating the structures */
+static void shmcb_get_header(void *, SHMCBHeader **);
+static BOOL shmcb_get_division(SHMCBHeader *, SHMCBQueue *, SHMCBCache *, unsigned int);
+static SHMCBIndex *shmcb_get_index(const SHMCBQueue *, unsigned int);
+static unsigned int shmcb_expire_division(server_rec *, SHMCBQueue *, SHMCBCache *);
+static BOOL shmcb_insert_encoded_session(server_rec *, SHMCBQueue *, SHMCBCache *, unsigned char *, unsigned int, unsigned char *, time_t);
+static SSL_SESSION *shmcb_lookup_session_id(server_rec *, SHMCBQueue *, SHMCBCache *, UCHAR *, int);
+static BOOL shmcb_remove_session_id(server_rec *, SHMCBQueue *, SHMCBCache *, UCHAR *, int);
+
+/*
+ * Data-alignment functions (a.k.a. avoidance tactics)
+ *
+ * NB: On HPUX (and possibly others) there is a *very* mischievous little
+ * "optimisation" in the compilers where it will convert the following;
+ *      memcpy(dest_ptr, &source, sizeof(unsigned int));
+ * (where dest_ptr is of type (unsigned int *) and source is (unsigned int))
+ * into;
+ *      *dest_ptr = source; (or *dest_ptr = *(&source), not sure).
+ * Either way, it completely destroys the whole point of these _safe_
+ * functions, because the assignment operation will fall victim to the
+ * architecture's byte-alignment dictations, whereas the memcpy (as a
+ * byte-by-byte copy) should not. sigh. So, if you're wondering about the
+ * apparently unnecessary conversions to (unsigned char *) in these
+ * functions, you now have an explanation. Don't just revert them back and
+ * say "ooh look, it still works" - if you try it on HPUX (well, 32-bit
+ * HPUX 11.00 at least) you may find it fails with a SIGBUS. :-(
+ */
+
+static unsigned int shmcb_get_safe_uint(unsigned int *ptr)
+{
+    unsigned char *from;
+    unsigned int ret;
+
+    from = (unsigned char *)ptr;
+    memcpy(&ret, from, sizeof(unsigned int));
+    return ret;
+}
+
+static void shmcb_set_safe_uint(unsigned int *ptr, unsigned int val)
+{
+    unsigned char *to, *from;
+
+    to = (unsigned char *)ptr;
+    from = (unsigned char *)(&val);
+    memcpy(to, from, sizeof(unsigned int));
+}
+
+#if 0 /* Unused so far */
+static unsigned long shmcb_get_safe_ulong(unsigned long *ptr)
+{
+    unsigned char *from;
+    unsigned long ret;
+
+    from = (unsigned char *)ptr;
+    memcpy(&ret, from, sizeof(unsigned long));
+    return ret;
+}
+
+static void shmcb_set_safe_ulong(unsigned long *ptr, unsigned long val)
+{
+    unsigned char *to, *from;
+
+    to = (unsigned char *)ptr;
+    from = (unsigned char *)(&val);
+    memcpy(to, from, sizeof(unsigned long));
+}
+#endif
+
+static time_t shmcb_get_safe_time(time_t * ptr)
+{
+    unsigned char *from;
+    time_t ret;
+
+    from = (unsigned char *)ptr;
+    memcpy(&ret, from, sizeof(time_t));
+    return ret;
+}
+
+static void shmcb_set_safe_time(time_t * ptr, time_t val)
+{
+    unsigned char *to, *from;
+
+    to = (unsigned char *)ptr;
+    from = (unsigned char *)(&val);
+    memcpy(to, from, sizeof(time_t));
+}
+
+/*
+**
+** High-Level "handlers" as per ssl_scache.c
+**
+*/
+
+static void *shmcb_malloc(size_t size)
+{
+    SSLModConfigRec *mc = myModConfig();
+    return ap_mm_malloc(mc->pSessionCacheDataMM, size);
+}
+
+void ssl_scache_shmcb_init(server_rec *s, pool *p)
+{
+    SSLModConfigRec *mc = myModConfig();
+    AP_MM *mm;
+    void *shm_segment = NULL;
+    int avail, avail_orig;
+
+    /*
+     * Create shared memory segment
+     */
+    if (mc->szSessionCacheDataFile == NULL) {
+        ssl_log(s, SSL_LOG_ERROR, "SSLSessionCache required");
+        ssl_die();
+    }
+    if ((mm = ap_mm_create(mc->nSessionCacheDataSize,
+                           mc->szSessionCacheDataFile)) == NULL) {
+        ssl_log(s, SSL_LOG_ERROR,
+                "Cannot allocate shared memory: %s", ap_mm_error());
+        ssl_die();
+    }
+    mc->pSessionCacheDataMM = mm;
+
+    /*
+     * Make sure the child processes have access to the underlying files
+     */
+    ap_mm_permission(mm, SSL_MM_FILE_MODE, ap_user_id, -1);
+
+    /*
+     * Create cache inside the shared memory segment
+     */
+    avail = avail_orig = ap_mm_available(mm);
+    ssl_log(s, SSL_LOG_TRACE, "Shared-memory segment has %u available",
+            avail);
+
+    /* 
+     * For some reason to do with MM's internal management, I can't
+     * allocate the full amount. Implement a reasonable form of trial
+     * and error and output trace information.
+     */
+    while ((shm_segment == NULL) && ((avail_orig - avail) * 100 < avail_orig)) {
+        shm_segment = shmcb_malloc(avail);
+        if (shm_segment == NULL) {
+            ssl_log(s, SSL_LOG_TRACE,
+                    "shmcb_malloc attempt for %u bytes failed", avail);
+            avail -= 2;
+        }
+    }
+    if (shm_segment == NULL) {
+        ssl_log(s, SSL_LOG_ERROR,
+                "Cannot allocate memory for the 'shmcb' session cache\n");
+        ssl_die();
+    }
+    ssl_log(s, SSL_LOG_TRACE, "shmcb_init allocated %u bytes of shared "
+            "memory", avail);
+    if (!shmcb_init_memory(s, shm_segment, avail)) {
+        ssl_log(s, SSL_LOG_ERROR,
+                "Failure initialising 'shmcb' shared memory");
+        ssl_die();
+    }
+    ssl_log(s, SSL_LOG_INFO, "Shared memory session cache initialised");
+
+    /* 
+     * Success ... we hack the memory block into place by cheating for
+     * now and stealing a member variable the original shared memory
+     * cache was using. :-)
+     */
+    mc->tSessionCacheDataTable = (table_t *) shm_segment;
+    return;
+}
+
+void ssl_scache_shmcb_kill(server_rec *s)
+{
+    SSLModConfigRec *mc = myModConfig();
+
+    if (mc->pSessionCacheDataMM != NULL) {
+        ap_mm_destroy(mc->pSessionCacheDataMM);
+        mc->pSessionCacheDataMM = NULL;
+    }
+    return;
+}
+
+BOOL ssl_scache_shmcb_store(server_rec *s, UCHAR * id, int idlen,
+                           time_t timeout, SSL_SESSION * pSession)
+{
+    SSLModConfigRec *mc = myModConfig();
+    void *shm_segment;
+    BOOL to_return = FALSE;
+
+    /* We've kludged our pointer into the other cache's member variable. */
+    shm_segment = (void *) mc->tSessionCacheDataTable;
+    ssl_mutex_on(s);
+    if (!shmcb_store_session(s, shm_segment, id, idlen, pSession, timeout))
+        /* in this cache engine, "stores" should never fail. */
+        ssl_log(s, SSL_LOG_ERROR, "'shmcb' code was unable to store a "
+                "session in the cache.");
+    else {
+        ssl_log(s, SSL_LOG_TRACE, "shmcb_store successful");
+        to_return = TRUE;
+    }
+    ssl_mutex_off(s);
+    return to_return;
+}
+
+SSL_SESSION *ssl_scache_shmcb_retrieve(server_rec *s, UCHAR * id, int idlen)
+{
+    SSLModConfigRec *mc = myModConfig();
+    void *shm_segment;
+    SSL_SESSION *pSession;
+
+    /* We've kludged our pointer into the other cache's member variable. */
+    shm_segment = (void *) mc->tSessionCacheDataTable;
+    ssl_mutex_on(s);
+    pSession = shmcb_retrieve_session(s, shm_segment, id, idlen);
+    ssl_mutex_off(s);
+    if (pSession)
+        ssl_log(s, SSL_LOG_TRACE, "shmcb_retrieve had a hit");
+    else {
+        ssl_log(s, SSL_LOG_TRACE, "shmcb_retrieve had a miss");
+        ssl_log(s, SSL_LOG_INFO, "Client requested a 'session-resume' but "
+                "we have no such session.");
+    }
+    return pSession;
+}
+
+void ssl_scache_shmcb_remove(server_rec *s, UCHAR * id, int idlen)
+{
+    SSLModConfigRec *mc = myModConfig();
+    void *shm_segment;
+
+    /* We've kludged our pointer into the other cache's member variable. */
+    shm_segment = (void *) mc->tSessionCacheDataTable;
+    shmcb_remove_session(s, shm_segment, id, idlen);
+}
+
+void ssl_scache_shmcb_expire(server_rec *s)
+{
+    /* NOP */
+    return;
+}
+
+void ssl_scache_shmcb_status(server_rec *s, pool *p,
+                            void (*func) (char *, void *), void *arg)
+{
+    SSLModConfigRec *mc = myModConfig();
+    SHMCBHeader *header;
+    SHMCBQueue queue;
+    SHMCBCache cache;
+    SHMCBIndex *idx;
+    void *shm_segment;
+    unsigned int loop, total, cache_total, non_empty_divisions;
+    int index_pct, cache_pct;
+    double expiry_total;
+    time_t average_expiry, now, max_expiry, min_expiry, idxexpiry;
+
+    ssl_log(s, SSL_LOG_TRACE, "inside ssl_scache_shmcb_status");
+
+    /* We've kludged our pointer into the other cache's member variable. */
+    shm_segment = (void *) mc->tSessionCacheDataTable;
+
+    /* Get the header structure. */
+    shmcb_get_header(shm_segment, &header);
+    total = cache_total = non_empty_divisions = 0;
+    average_expiry = max_expiry = min_expiry = 0;
+    expiry_total = 0;
+
+    /* It may seem strange to grab "now" at this point, but in theory
+     * we should never have a negative threshold but grabbing "now" after
+     * the loop (which performs expiries) could allow that chance. */
+    now = time(NULL);
+    for (loop = 0; loop <= header->division_mask; loop++) {
+        if (shmcb_get_division(header, &queue, &cache, loop)) {
+            shmcb_expire_division(s, &queue, &cache);
+            total += shmcb_get_safe_uint(queue.pos_count);
+            cache_total += shmcb_get_safe_uint(cache.pos_count);
+            if (shmcb_get_safe_uint(queue.pos_count) > 0) {
+                idx = shmcb_get_index(&queue,
+                                     shmcb_get_safe_uint(queue.first_pos));
+                non_empty_divisions++;
+                idxexpiry = shmcb_get_safe_time(&(idx->expires));
+                expiry_total += (double) idxexpiry;
+                max_expiry = (idxexpiry > max_expiry ? idxexpiry :
+                              max_expiry);
+                if (min_expiry == 0)
+                    min_expiry = idxexpiry;
+                else
+                    min_expiry = (idxexpiry < min_expiry ? idxexpiry :
+                                  min_expiry);
+            }
+        }
+    }
+    index_pct = (100 * total) / (header->index_num * (header->division_mask + 1));
+    cache_pct = (100 * cache_total) / (header->cache_data_size * (header->division_mask + 1));
+    func(ap_psprintf(p, "cache type: <b>SHMCB</b>, shared memory: <b>%d</b> "
+                     "bytes, current sessions: <b>%d</b><br>",
+                     mc->nSessionCacheDataSize, total), arg);
+    func(ap_psprintf(p, "sub-caches: <b>%d</b>, indexes per sub-cache: "
+                     "<b>%d</b><br>", (int) header->division_mask + 1,
+                     (int) header->index_num), arg);
+    if (non_empty_divisions != 0) {
+        average_expiry = (time_t)(expiry_total / (double)non_empty_divisions);
+        func(ap_psprintf(p, "time left on oldest entries' SSL sessions: "), arg);
+        if (now < average_expiry)
+            func(ap_psprintf(p, "avg: <b>%d</b> seconds, (range: %d...%d)<br>",
+                            (int)(average_expiry - now), (int) (min_expiry - now),
+                            (int)(max_expiry - now)), arg);
+        else
+            func(ap_psprintf(p, "expiry threshold: <b>Calculation Error!</b>" 
+                             "<br>"), arg);
+
+    }
+    func(ap_psprintf(p, "index usage: <b>%d%%</b>, cache usage: <b>%d%%</b>"
+                     "<br>", index_pct, cache_pct), arg);
+    func(ap_psprintf(p, "total sessions stored since starting: <b>%lu</b><br>",
+                     header->num_stores), arg);
+    func(ap_psprintf(p, "total sessions expired since starting: <b>%lu</b><br>",
+                     header->num_expiries), arg);
+    func(ap_psprintf(p, "total (pre-expiry) sessions scrolled out of the "
+                     "cache: <b>%lu</b><br>", header->num_scrolled), arg);
+    func(ap_psprintf(p, "total retrieves since starting: <b>%lu</b> hit, "
+                     "<b>%lu</b> miss<br>", header->num_retrieves_hit,
+                     header->num_retrieves_miss), arg);
+    func(ap_psprintf(p, "total removes since starting: <b>%lu</b> hit, "
+                     "<b>%lu</b> miss<br>", header->num_removes_hit,
+                     header->num_removes_miss), arg);
+    ssl_log(s, SSL_LOG_TRACE, "leaving shmcb_status");
+    return;
+}
+
+/*
+**
+** Memory manipulation and low-level cache operations 
+**
+*/
+
+static BOOL shmcb_init_memory(
+    server_rec *s, void *shm_mem,
+    unsigned int shm_mem_size)
+{
+    SHMCBHeader *header;
+    SHMCBQueue queue;
+    SHMCBCache cache;
+    unsigned int temp, loop, granularity;
+
+    ssl_log(s, SSL_LOG_TRACE, "entered shmcb_init_memory()");
+
+    /* Calculate some sizes... */
+    temp = sizeof(SHMCBHeader);
+
+    /* If the segment is ridiculously too small, bail out */
+    if (shm_mem_size < (2*temp)) {
+        ssl_log(s, SSL_LOG_ERROR, "shared memory segment too small");
+        return FALSE;
+    }
+
+    /* Make temp the amount of memory without the header */
+    temp = shm_mem_size - temp;
+
+    /* Work on the basis that you need 10 bytes index for each session
+     * (approx 150 bytes), which is to divide temp by 160 - and then
+     * make sure we err on having too index space to burn even when
+     * the cache is full, which is a lot less stupid than having
+     * having not enough index space to utilise the whole cache!. */
+    temp /= 120;
+    ssl_log(s, SSL_LOG_TRACE, "for %u bytes, recommending %u indexes",
+            shm_mem_size, temp);
+
+    /* We should divide these indexes evenly amongst the queues. Try
+     * to get it so that there are roughly half the number of divisions
+     * as there are indexes in each division. */
+    granularity = 256;
+    while ((temp / granularity) < (2 * granularity))
+        granularity /= 2;
+
+    /* So we have 'granularity' divisions, set 'temp' equal to the
+     * number of indexes in each division. */
+    temp /= granularity;
+
+    /* Too small? Bail ... */
+    if (temp < 5) {
+        ssl_log(s, SSL_LOG_ERROR, "shared memory segment too small");
+        return FALSE;
+    }
+
+    /* OK, we're sorted - from here on in, the return should be TRUE */
+    header = (SHMCBHeader *)shm_mem;
+    header->division_mask = (unsigned char)(granularity - 1);
+    header->division_offset = sizeof(SHMCBHeader);
+    header->index_num = temp;
+    header->index_offset = (2 * sizeof(unsigned int));
+    header->index_size = sizeof(SHMCBIndex);
+    header->queue_size = header->index_offset +
+                         (header->index_num * header->index_size);
+
+    /* Now calculate the space for each division */
+    temp = shm_mem_size - header->division_offset;
+    header->division_size = temp / granularity;
+
+    /* Calculate the space left in each division for the cache */
+    temp -= header->queue_size;
+    header->cache_data_offset = (2 * sizeof(unsigned int));
+    header->cache_data_size = header->division_size -
+                              header->queue_size - header->cache_data_offset;
+
+    /* Output trace info */
+    ssl_log(s, SSL_LOG_TRACE, "shmcb_init_memory choices follow");
+    ssl_log(s, SSL_LOG_TRACE, "division_mask = 0x%02X", header->division_mask);
+    ssl_log(s, SSL_LOG_TRACE, "division_offset = %u", header->division_offset);
+    ssl_log(s, SSL_LOG_TRACE, "division_size = %u", header->division_size);
+    ssl_log(s, SSL_LOG_TRACE, "queue_size = %u", header->queue_size);
+    ssl_log(s, SSL_LOG_TRACE, "index_num = %u", header->index_num);
+    ssl_log(s, SSL_LOG_TRACE, "index_offset = %u", header->index_offset);
+    ssl_log(s, SSL_LOG_TRACE, "index_size = %u", header->index_size);
+    ssl_log(s, SSL_LOG_TRACE, "cache_data_offset = %u", header->cache_data_offset);
+    ssl_log(s, SSL_LOG_TRACE, "cache_data_size = %u", header->cache_data_size);
+
+    /* The header is done, make the caches empty */
+    for (loop = 0; loop < granularity; loop++) {
+        if (!shmcb_get_division(header, &queue, &cache, loop))
+            ssl_log(s, SSL_LOG_ERROR, "shmcb_init_memory, " "internal error");
+        shmcb_set_safe_uint(cache.first_pos, 0);
+        shmcb_set_safe_uint(cache.pos_count, 0);
+        shmcb_set_safe_uint(queue.first_pos, 0);
+        shmcb_set_safe_uint(queue.pos_count, 0);
+    }
+
+    ssl_log(s, SSL_LOG_TRACE, "leaving shmcb_init_memory()");
+    return TRUE;
+}
+
+static BOOL shmcb_store_session(
+    server_rec *s, void *shm_segment, UCHAR * id,
+    int idlen, SSL_SESSION * pSession,
+    time_t timeout)
+{
+    SHMCBHeader *header;
+    SHMCBQueue queue;
+    SHMCBCache cache;
+    unsigned char masked_index;
+    unsigned char encoded[SSL_SESSION_MAX_DER];
+    unsigned char *ptr_encoded;
+    unsigned int len_encoded;
+    time_t expiry_time;
+
+    ssl_log(s, SSL_LOG_TRACE, "inside shmcb_store_session");
+
+    /* Get the header structure, which division this session will fall into etc. */
+    shmcb_get_header(shm_segment, &header);
+    masked_index = pSession->session_id[0] & header->division_mask;
+    ssl_log(s, SSL_LOG_TRACE, "session_id[0]=%u, masked index=%u",
+            pSession->session_id[0], masked_index);
+    if (!shmcb_get_division(header, &queue, &cache, (unsigned int)masked_index)) {
+        ssl_log(s, SSL_LOG_ERROR, "shmcb_store_session, " "internal error");
+        return FALSE;
+    }
+
+    /* Serialise the session, work out how much we're dealing
+     * with. NB: This check could be removed if we're not paranoid
+     * or we find some assurance that it will never be necessary. */
+    len_encoded = i2d_SSL_SESSION(pSession, NULL);
+    if (len_encoded > SSL_SESSION_MAX_DER) {
+        ssl_log(s, SSL_LOG_ERROR, "session is too big (%u bytes)",
+                len_encoded);
+        return FALSE;
+    }
+    ptr_encoded = encoded;
+    len_encoded = i2d_SSL_SESSION(pSession, &ptr_encoded);
+    expiry_time = timeout;
+    if (!shmcb_insert_encoded_session(s, &queue, &cache, encoded,
+                                     len_encoded, pSession->session_id,
+                                     expiry_time)) {
+        ssl_log(s, SSL_LOG_ERROR, "can't store a session!");
+        return FALSE;
+    }
+    ssl_log(s, SSL_LOG_TRACE, "leaving shmcb_store successfully");
+    header->num_stores++;
+    return TRUE;
+}
+
+static SSL_SESSION *shmcb_retrieve_session(
+    server_rec *s, void *shm_segment,
+    UCHAR * id, int idlen)
+{
+    SHMCBHeader *header;
+    SHMCBQueue queue;
+    SHMCBCache cache;
+    unsigned char masked_index;
+    SSL_SESSION *pSession;
+
+    ssl_log(s, SSL_LOG_TRACE, "inside shmcb_retrieve_session");
+    if (idlen < 2) {
+        ssl_log(s, SSL_LOG_ERROR, "unusably short session_id provided "
+                "(%u bytes)", idlen);
+        return FALSE;
+    }
+
+    /* Get the header structure, which division this session lookup
+     * will come from etc. */
+    shmcb_get_header(shm_segment, &header);
+    masked_index = id[0] & header->division_mask;
+    ssl_log(s, SSL_LOG_TRACE, "id[0]=%u, masked index=%u", id[0],
+            masked_index);
+    if (!shmcb_get_division(header, &queue, &cache, (unsigned int) masked_index)) {
+        ssl_log(s, SSL_LOG_ERROR, "shmcb_retrieve_session, " "internal error");
+        header->num_retrieves_miss++;
+        return FALSE;
+    }
+
+    /* Get the session corresponding to the session_id or NULL if it
+     * doesn't exist (or is flagged as "removed"). */
+    pSession = shmcb_lookup_session_id(s, &queue, &cache, id, idlen);
+    if (pSession)
+        header->num_retrieves_hit++;
+    else
+        header->num_retrieves_miss++;
+    ssl_log(s, SSL_LOG_TRACE, "leaving shmcb_retrieve_session");
+    return pSession;
+}
+
+static BOOL shmcb_remove_session(
+    server_rec *s, void *shm_segment,
+    UCHAR * id, int idlen)
+{
+    SHMCBHeader *header;
+    SHMCBQueue queue;
+    SHMCBCache cache;
+    unsigned char masked_index;
+    BOOL res;
+
+    ssl_log(s, SSL_LOG_TRACE, "inside shmcb_remove_session");
+    if (id == NULL) {
+        ssl_log(s, SSL_LOG_ERROR, "remove called with NULL session_id!");
+        return FALSE;
+    }
+
+    /* Get the header structure, which division this session remove
+     * will happen in etc. */
+    shmcb_get_header(shm_segment, &header);
+    masked_index = id[0] & header->division_mask;
+    ssl_log(s, SSL_LOG_TRACE, "id[0]=%u, masked index=%u",
+            id[0], masked_index);
+    if (!shmcb_get_division(header, &queue, &cache, (unsigned int)masked_index)) {
+        ssl_log(s, SSL_LOG_ERROR, "shmcb_remove_session, internal error");
+        header->num_removes_miss++;
+        return FALSE;
+    }
+    res = shmcb_remove_session_id(s, &queue, &cache, id, idlen);
+    if (res)
+        header->num_removes_hit++;
+    else
+        header->num_removes_miss++;
+    ssl_log(s, SSL_LOG_TRACE, "leaving shmcb_remove_session");
+    return res;
+}
+
+
+/* 
+**
+** Weirdo cyclic buffer functions
+**
+*/
+
+/* This gets used in the cyclic "index array" (in the 'Queue's) and
+ * in the cyclic 'Cache's too ... you provide the "width" of the
+ * cyclic store, the starting position and how far to move (with
+ * wrapping if necessary). Basically it's addition modulo buf_size. */
+static unsigned int shmcb_cyclic_increment(
+    unsigned int buf_size,
+    unsigned int start_pos,
+    unsigned int to_add)
+{
+    start_pos += to_add;
+    while (start_pos >= buf_size)
+        start_pos -= buf_size;
+    return start_pos;
+}
+
+/* Given two positions in a cyclic buffer, calculate the "distance".
+ * This is to cover the case ("non-trivial") where the 'next' offset
+ * is to the left of the 'start' offset. NB: This calculates the
+ * space inclusive of one end-point but not the other. There is an
+ * ambiguous case (which is why we use the <start_pos,offset>
+ * coordinate system rather than <start_pos,end_pos> one) when 'start'
+ * is the same as 'next'. It could indicate the buffer is full or it
+ * can indicate the buffer is empty ... I choose the latter as it's
+ * easier and usually necessary to check if the buffer is full anyway
+ * before doing incremental logic (which is this useful for), but we
+ * definitely need the empty case handled - in fact it's our starting
+ * state!! */
+static unsigned int shmcb_cyclic_space(
+    unsigned int buf_size,
+    unsigned int start_offset,
+    unsigned int next_offset)
+{
+    /* Is it the trivial case? */
+    if (start_offset <= next_offset)
+        return (next_offset - start_offset);              /* yes */
+    else
+        return ((buf_size - start_offset) + next_offset); /* no */
+}
+
+/* A "normal-to-cyclic" memcpy ... this takes a linear block of
+ * memory and copies it onto a cyclic buffer. The purpose and
+ * function of this is pretty obvious, you need to cover the case
+ * that the destination (cyclic) buffer has to wrap round. */
+static void shmcb_cyclic_ntoc_memcpy(
+    unsigned int buf_size,
+    unsigned char *data,
+    unsigned int dest_offset,
+    unsigned char *src, unsigned int src_len)
+{
+    /* Can it be copied all in one go? */
+    if (dest_offset + src_len < buf_size)
+        /* yes */
+        memcpy(data + dest_offset, src, src_len);
+    else {
+        /* no */
+        memcpy(data + dest_offset, src, buf_size - dest_offset);
+        memcpy(data, src + buf_size - dest_offset,
+               src_len + dest_offset - buf_size);
+    }
+    return;
+}
+
+/* A "cyclic-to-normal" memcpy ... given the last function, this
+ * one's purpose is clear, it copies out of a cyclic buffer handling
+ * wrapping. */
+static void shmcb_cyclic_cton_memcpy(
+    unsigned int buf_size,
+    unsigned char *dest,
+    unsigned char *data,
+    unsigned int src_offset,
+    unsigned int src_len)
+{
+    /* Can it be copied all in one go? */
+    if (src_offset + src_len < buf_size)
+        /* yes */
+        memcpy(dest, data + src_offset, src_len);
+    else {
+        /* no */
+        memcpy(dest, data + src_offset, buf_size - src_offset);
+        memcpy(dest + buf_size - src_offset, data,
+               src_len + src_offset - buf_size);
+    }
+    return;
+}
+
+/* Here's the cool hack that makes it all work ... by simply
+ * making the first collection of bytes *be* our header structure
+ * (casting it into the C structure), we have the perfect way to
+ * maintain state in a shared-memory session cache from one call
+ * (and process) to the next, use the shared memory itself! The
+ * original mod_ssl shared-memory session cache uses variables
+ * inside the context, but we simply use that for storing the
+ * pointer to the shared memory itself. And don't forget, after
+ * Apache's initialisation, this "header" is constant/read-only
+ * so we can read it outside any locking.
+ * <grin> - sometimes I just *love* coding y'know?!  */
+static void shmcb_get_header(void *shm_mem, SHMCBHeader **header)
+{
+    *header = (SHMCBHeader *)shm_mem;
+    return;
+}
+
+/* This is what populates our "interesting" structures. Given a
+ * pointer to the header, and an index into the appropriate
+ * division (this must have already been masked using the
+ * division_mask by the caller!), we can populate the provided
+ * SHMCBQueue and SHMCBCache structures with values and
+ * pointers to the underlying shared memory. Upon returning
+ * (if not FALSE), the caller can meddle with the pointer
+ * values and they will map into the shared-memory directly,
+ * as such there's no need to "free" or "set" the Queue or
+ * Cache values, they were themselves references to the *real*
+ * data. */
+static BOOL shmcb_get_division(
+    SHMCBHeader *header, SHMCBQueue *queue,
+    SHMCBCache *cache, unsigned int idx)
+{
+    unsigned char *pQueue;
+    unsigned char *pCache;
+
+    /* bounds check */
+    if (idx > (unsigned int) header->division_mask)
+        return FALSE;
+
+    /* Locate the blocks of memory storing the corresponding data */
+    pQueue = ((unsigned char *) header) + header->division_offset +
+        (idx * header->division_size);
+    pCache = pQueue + header->queue_size;
+
+    /* Populate the structures with appropriate pointers */
+    queue->first_pos = (unsigned int *) pQueue;
+
+    /* Our structures stay packed, no matter what the system's
+     * data-alignment regime is. */
+    queue->pos_count = (unsigned int *) (pQueue + sizeof(unsigned int));
+    queue->indexes = (SHMCBIndex *) (pQueue + (2 * sizeof(unsigned int)));
+    cache->first_pos = (unsigned int *) pCache;
+    cache->pos_count = (unsigned int *) (pCache + sizeof(unsigned int));
+    cache->data = (unsigned char *) (pCache + (2 * sizeof(unsigned int)));
+    queue->header = cache->header = header;
+
+    return TRUE;
+}
+
+/* This returns a pointer to the piece of shared memory containing
+ * a specified 'Index'. SHMCBIndex, like SHMCBHeader, is a fixed
+ * width non-referencing structure of primitive types that can be
+ * cast onto the corresponding block of shared memory. Thus, by
+ * returning a cast pointer to that section of shared memory, the
+ * caller can read and write values to and from the "structure" and
+ * they are actually reading and writing the underlying shared
+ * memory. */
+static SHMCBIndex *shmcb_get_index(
+    const SHMCBQueue *queue, unsigned int idx)
+{
+    /* bounds check */
+    if (idx > (unsigned int) queue->header->index_num)
+        return NULL;
+
+    /* Return a pointer to the index. NB: I am being horribly pendantic
+     * here so as to avoid any potential data-alignment assumptions being
+     * placed on the pointer arithmetic by the compiler (sigh). */
+    return (SHMCBIndex *)(((unsigned char *) queue->indexes) +
+                          (idx * sizeof(SHMCBIndex)));
+}
+
+/* This functions rolls expired cache (and index) entries off the front
+ * of the cyclic buffers in a division. The function returns the number
+ * of expired sessions. */
+static unsigned int shmcb_expire_division(
+    server_rec *s, SHMCBQueue *queue, SHMCBCache *cache)
+{
+    SHMCBIndex *idx;
+    time_t now;
+    unsigned int loop, index_num, pos_count, new_pos;
+    SHMCBHeader *header;
+
+    ssl_log(s, SSL_LOG_TRACE, "entering shmcb_expire_division");
+
+    /* We must calculate num and space ourselves based on expiry times. */
+    now = time(NULL);
+    loop = 0;
+    new_pos = shmcb_get_safe_uint(queue->first_pos);
+
+    /* Cache useful values */
+    header = queue->header;
+    index_num = header->index_num;
+    pos_count = shmcb_get_safe_uint(queue->pos_count);
+    while (loop < pos_count) {
+        idx = shmcb_get_index(queue, new_pos);
+        if (shmcb_get_safe_time(&(idx->expires)) > now)
+            /* it hasn't expired yet, we're done iterating */
+            break;
+        /* This one should be expired too. Shift to the next entry. */
+        loop++;
+        new_pos = shmcb_cyclic_increment(index_num, new_pos, 1);
+    }
+
+    /* Find the new_offset and make the expiries happen. */
+    if (loop > 0) {
+        ssl_log(s, SSL_LOG_TRACE, "will be expiring %u sessions", loop);
+        /* We calculate the new_offset by "peeking" (or in the
+         * case it's the last entry, "sneaking" ;-). */
+        if (loop == pos_count) {
+            /* We are expiring everything! This is easy to do... */
+            shmcb_set_safe_uint(queue->pos_count, 0);
+            shmcb_set_safe_uint(cache->pos_count, 0);
+        }
+        else {
+            /* The Queue is easy to adjust */
+            shmcb_set_safe_uint(queue->pos_count,
+                               shmcb_get_safe_uint(queue->pos_count) - loop);
+            shmcb_set_safe_uint(queue->first_pos, new_pos);
+            /* peek to the start of the next session */
+            idx = shmcb_get_index(queue, new_pos);
+            /* We can use shmcb_cyclic_space because we've guaranteed 
+             * we don't fit the ambiguous full/empty case. */
+            shmcb_set_safe_uint(cache->pos_count,
+                               shmcb_get_safe_uint(cache->pos_count) -
+                               shmcb_cyclic_space(header->cache_data_size,
+                                                  shmcb_get_safe_uint(cache->first_pos),
+                                                  shmcb_get_safe_uint(&(idx->offset))));
+            shmcb_set_safe_uint(cache->first_pos, shmcb_get_safe_uint(&(idx->offset)));
+        }
+        ssl_log(s, SSL_LOG_TRACE, "we now have %u sessions",
+                shmcb_get_safe_uint(queue->pos_count));
+    }
+    header->num_expiries += loop;
+    return loop;
+}
+
+/* Inserts a new encoded session into a queue/cache pair - expiring
+ * (early or otherwise) any leading sessions as necessary to ensure
+ * there is room. An error return (FALSE) should only happen in the
+ * event of surreal values being passed on, or ridiculously small
+ * cache sizes. NB: For tracing purposes, this function is also given
+ * the server_rec to allow "ssl_log()". */
+static BOOL shmcb_insert_encoded_session(
+    server_rec *s, SHMCBQueue * queue,
+    SHMCBCache * cache,
+    unsigned char *encoded,
+    unsigned int encoded_len,
+    unsigned char *session_id,
+    time_t expiry_time)
+{
+    SHMCBHeader *header;
+    SHMCBIndex *idx = NULL;
+    unsigned int gap, new_pos, loop, new_offset;
+    int need;
+
+    ssl_log(s, SSL_LOG_TRACE, "entering shmcb_insert_encoded_session, "
+            "*queue->pos_count = %u", shmcb_get_safe_uint(queue->pos_count));
+
+    /* If there's entries to expire, ditch them first thing. */
+    shmcb_expire_division(s, queue, cache);
+    header = cache->header;
+    gap = header->cache_data_size - shmcb_get_safe_uint(cache->pos_count);
+    if (gap < encoded_len) {
+        new_pos = shmcb_get_safe_uint(queue->first_pos);
+        loop = 0;
+        need = (int) encoded_len - (int) gap;
+        while ((need > 0) && (loop + 1 < shmcb_get_safe_uint(queue->pos_count))) {
+            new_pos = shmcb_cyclic_increment(header->index_num, new_pos, 1);
+            loop += 1;
+            idx = shmcb_get_index(queue, new_pos);
+            need = (int) encoded_len - (int) gap -
+                shmcb_cyclic_space(header->cache_data_size,
+                                   shmcb_get_safe_uint(cache->first_pos),
+                                   shmcb_get_safe_uint(&(idx->offset)));
+        }
+        if (loop > 0) {
+            ssl_log(s, SSL_LOG_TRACE, "about to scroll %u sessions from %u",
+                    loop, shmcb_get_safe_uint(queue->pos_count));
+            /* We are removing "loop" items from the cache. */
+            shmcb_set_safe_uint(cache->pos_count,
+                                shmcb_get_safe_uint(cache->pos_count) -
+                                shmcb_cyclic_space(header->cache_data_size,
+                                                   shmcb_get_safe_uint(cache->first_pos),
+                                                   shmcb_get_safe_uint(&(idx->offset))));
+            shmcb_set_safe_uint(cache->first_pos, shmcb_get_safe_uint(&(idx->offset)));
+            shmcb_set_safe_uint(queue->pos_count, shmcb_get_safe_uint(queue->pos_count) - loop);
+            shmcb_set_safe_uint(queue->first_pos, new_pos);
+            ssl_log(s, SSL_LOG_TRACE, "now only have %u sessions",
+                    shmcb_get_safe_uint(queue->pos_count));
+            /* Update the stats!!! */
+            header->num_scrolled += loop;
+        }
+    }
+
+    /* probably unecessary checks, but I'll leave them until this code
+     * is verified. */
+    if (shmcb_get_safe_uint(cache->pos_count) + encoded_len >
+        header->cache_data_size) {
+        ssl_log(s, SSL_LOG_ERROR, "shmcb_insert_encoded_session, "
+                "internal error");
+        return FALSE;
+    }
+    if (shmcb_get_safe_uint(queue->pos_count) == header->index_num) {
+        ssl_log(s, SSL_LOG_ERROR, "shmcb_insert_encoded_session, "
+                "internal error");
+        return FALSE;
+    }
+    ssl_log(s, SSL_LOG_TRACE, "we have %u bytes and %u indexes free - "
+            "enough", header->cache_data_size -
+            shmcb_get_safe_uint(cache->pos_count), header->index_num -
+            shmcb_get_safe_uint(queue->pos_count));
+
+
+    /* HERE WE ASSUME THAT THE NEW SESSION SHOULD GO ON THE END! I'M NOT
+     * CHECKING WHETHER IT SHOULD BE GENUINELY "INSERTED" SOMEWHERE.
+     *
+     * We either fix that, or find out at a "higher" (read "mod_ssl")
+     * level whether it is possible to have distinct session caches for
+     * any attempted tomfoolery to do with different session timeouts.
+     * Knowing in advance that we can have a cache-wide constant timeout
+     * would make this stuff *MUCH* more efficient. Mind you, it's very
+     * efficient right now because I'm ignoring this problem!!!
+     */
+
+    /* Increment to the first unused byte */
+    new_offset = shmcb_cyclic_increment(header->cache_data_size,
+                                        shmcb_get_safe_uint(cache->first_pos),
+                                        shmcb_get_safe_uint(cache->pos_count));
+    /* Copy the DER-encoded session into place */
+    shmcb_cyclic_ntoc_memcpy(header->cache_data_size, cache->data,
+                            new_offset, encoded, encoded_len);
+    /* Get the new index that this session is stored in. */
+    new_pos = shmcb_cyclic_increment(header->index_num,
+                                     shmcb_get_safe_uint(queue->first_pos),
+                                     shmcb_get_safe_uint(queue->pos_count));
+    ssl_log(s, SSL_LOG_TRACE, "storing in index %u, at offset %u", new_pos,
+            new_offset);
+    idx = shmcb_get_index(queue, new_pos);
+    if (idx == NULL) {
+        ssl_log(s, SSL_LOG_ERROR, "shmcb_insert_encoded_session, "
+                "internal error");
+        return FALSE;
+    }
+    memset(idx, 0, sizeof(SHMCBIndex));
+    shmcb_set_safe_time(&(idx->expires), expiry_time);
+    shmcb_set_safe_uint(&(idx->offset), new_offset);
+
+    /* idx->removed = (unsigned char)0; */ /* Not needed given the memset above. */
+    idx->s_id2 = session_id[1];
+    ssl_log(s, SSL_LOG_TRACE, "session_id[0]=%u, idx->s_id2=%u",
+            session_id[0], session_id[1]);
+
+    /* All that remains is to adjust the cache's and queue's "pos_count"s. */
+    shmcb_set_safe_uint(cache->pos_count,
+                       shmcb_get_safe_uint(cache->pos_count) + encoded_len);
+    shmcb_set_safe_uint(queue->pos_count,
+                       shmcb_get_safe_uint(queue->pos_count) + 1);
+
+    /* And just for good debugging measure ... */
+    ssl_log(s, SSL_LOG_TRACE, "leaving now with %u bytes in the cache and "
+            "%u indexes", shmcb_get_safe_uint(cache->pos_count),
+            shmcb_get_safe_uint(queue->pos_count));
+    ssl_log(s, SSL_LOG_TRACE, "leaving shmcb_insert_encoded_session");
+    return TRUE;
+}
+
+/* Performs a lookup into a queue/cache pair for a
+ * session_id. If found, the session is deserialised
+ * and returned, otherwise NULL. */
+static SSL_SESSION *shmcb_lookup_session_id(
+    server_rec *s, SHMCBQueue *queue,
+    SHMCBCache *cache, UCHAR *id,
+    int idlen)
+{
+    unsigned char tempasn[SSL_SESSION_MAX_DER];
+    SHMCBIndex *idx;
+    SHMCBHeader *header;
+    SSL_SESSION *pSession = NULL;
+    unsigned int curr_pos, loop, count;
+    unsigned char *ptr;
+    time_t now;
+
+    ssl_log(s, SSL_LOG_TRACE, "entering shmcb_lookup_session_id");
+
+    /* If there are entries to expire, ditch them first thing. */
+    shmcb_expire_division(s, queue, cache);
+    now = time(NULL);
+    curr_pos = shmcb_get_safe_uint(queue->first_pos);
+    count = shmcb_get_safe_uint(queue->pos_count);
+    header = queue->header;
+    for (loop = 0; loop < count; loop++) {
+        ssl_log(s, SSL_LOG_TRACE, "loop=%u, count=%u, curr_pos=%u",
+                loop, count, curr_pos);
+        idx = shmcb_get_index(queue, curr_pos);
+        ssl_log(s, SSL_LOG_TRACE, "idx->s_id2=%u, id[1]=%u, offset=%u",
+                idx->s_id2, id[1], shmcb_get_safe_uint(&(idx->offset)));
+        /* Only look into the session further if;
+         * (a) the second byte of the session_id matches,
+         * (b) the "removed" flag isn't set,
+         * (c) the session hasn't expired yet.
+         * We do (c) like this so that it saves us having to
+         * do natural expiries ... naturally expired sessions
+         * scroll off the front anyway when the cache is full and
+         * "rotating", the only real issue that remains is the
+         * removal or disabling of forcibly killed sessions. */
+        if ((idx->s_id2 == id[1]) && !idx->removed &&
+            (shmcb_get_safe_time(&(idx->expires)) > now)) {
+            ssl_log(s, SSL_LOG_TRACE, "at index %u, found possible "
+                    "session match", curr_pos);
+            shmcb_cyclic_cton_memcpy(header->cache_data_size,
+                                     tempasn, cache->data,
+                                     shmcb_get_safe_uint(&(idx->offset)),
+                                     SSL_SESSION_MAX_DER);
+            ptr = tempasn;
+            pSession = d2i_SSL_SESSION(NULL, &ptr, SSL_SESSION_MAX_DER);
+            if (pSession == NULL) {
+                ssl_log(s, SSL_LOG_ERROR, "scach2_lookup_"
+                        "session_id, internal error");
+                return NULL;
+            }
+            if ((pSession->session_id_length == idlen) &&
+                (memcmp(pSession->session_id, id, idlen) == 0)) {
+                ssl_log(s, SSL_LOG_TRACE, "a match!");
+                return pSession;
+            }
+            ssl_log(s, SSL_LOG_TRACE, "not a match");
+            SSL_SESSION_free(pSession);
+            pSession = NULL;
+        }
+        curr_pos = shmcb_cyclic_increment(header->index_num, curr_pos, 1);
+    }
+    ssl_log(s, SSL_LOG_TRACE, "no matching sessions were found");
+    return NULL;
+}
+
+static BOOL shmcb_remove_session_id(
+    server_rec *s, SHMCBQueue *queue,
+    SHMCBCache *cache, UCHAR *id, int idlen)
+{
+    unsigned char tempasn[SSL_SESSION_MAX_DER];
+    SSL_SESSION *pSession = NULL;
+    SHMCBIndex *idx;
+    SHMCBHeader *header;
+    unsigned int curr_pos, loop, count;
+    unsigned char *ptr;
+    BOOL to_return = FALSE;
+
+    ssl_log(s, SSL_LOG_TRACE, "entering shmcb_remove_session_id");
+
+    /* If there's entries to expire, ditch them first thing. */
+    /* shmcb_expire_division(s, queue, cache); */
+
+    /* Regarding the above ... hmmm ... I know my expiry code is slightly
+     * "faster" than all this remove stuff ... but if the higher level
+     * code calls a "remove" operation (and this *only* seems to happen
+     * when it has spotted an expired session before we had a chance to)
+     * then it should get credit for a remove (stats-wise). Also, in the
+     * off-chance that the server *requests* a renegotiate and wants to
+     * wipe the session clean we should give that priority over our own
+     * routine expiry handling. So I've moved the expiry check to *after*
+     * this general remove stuff. */
+    curr_pos = shmcb_get_safe_uint(queue->first_pos);
+    count = shmcb_get_safe_uint(queue->pos_count);
+    header = cache->header;
+    for (loop = 0; loop < count; loop++) {
+        ssl_log(s, SSL_LOG_TRACE, "loop=%u, count=%u, curr_pos=%u",
+                loop, count, curr_pos);
+        idx = shmcb_get_index(queue, curr_pos);
+        ssl_log(s, SSL_LOG_TRACE, "idx->s_id2=%u, id[1]=%u", idx->s_id2,
+                id[1]);
+        /* Only look into the session further if the second byte of the
+         * session_id matches. */
+        if (idx->s_id2 == id[1]) {
+            ssl_log(s, SSL_LOG_TRACE, "at index %u, found possible "
+                    "session match", curr_pos);
+            shmcb_cyclic_cton_memcpy(header->cache_data_size,
+                                     tempasn, cache->data,
+                                     shmcb_get_safe_uint(&(idx->offset)),
+                                     SSL_SESSION_MAX_DER);
+            ptr = tempasn;
+            pSession = d2i_SSL_SESSION(NULL, &ptr, SSL_SESSION_MAX_DER);
+            if (pSession == NULL) {
+                ssl_log(s, SSL_LOG_ERROR, "shmcb_remove_session_id, "
+                        "internal error");
+                goto end;
+            }
+            if ((pSession->session_id_length == idlen) 
+                 && (memcmp(id, pSession->session_id, idlen) == 0)) {
+                ssl_log(s, SSL_LOG_TRACE, "a match!");
+                /* Scrub out this session "quietly" */
+                idx->removed = (unsigned char) 1;
+                SSL_SESSION_free(pSession);
+                to_return = TRUE;
+                goto end;
+            }
+            ssl_log(s, SSL_LOG_TRACE, "not a match");
+            SSL_SESSION_free(pSession);
+            pSession = NULL;
+        }
+        curr_pos = shmcb_cyclic_increment(header->index_num, curr_pos, 1);
+    }
+    ssl_log(s, SSL_LOG_TRACE, "no matching sessions were found");
+
+    /* If there's entries to expire, ditch them now. */
+    shmcb_expire_division(s, queue, cache);
+end:
+    ssl_log(s, SSL_LOG_TRACE, "leaving shmcb_remove_session_id");
+    return to_return;
+}
+
diff --git a/modules/ssl/ssl_scache_shmht.c b/modules/ssl/ssl_scache_shmht.c
new file mode 100644 (file)
index 0000000..18e688a
--- /dev/null
@@ -0,0 +1,347 @@
+/*                      _             _
+**  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
+** | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
+** | | | | | | (_) | (_| |   \__ \__ \ |  www.modssl.org
+** |_| |_| |_|\___/ \__,_|___|___/___/_|  ftp.modssl.org
+**                      |_____|
+**  ssl_scache_shmht.c
+**  Session Cache via Shared Memory (Hash Table Variant)
+*/
+
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ *    nor may "mod_ssl" appear in their names without prior
+ *    written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include "mod_ssl.h"
+
+/*
+ *  Wrapper functions for table library which resemble malloc(3) & Co 
+ *  but use the variants from the MM shared memory library.
+ */
+
+static void *ssl_scache_shmht_malloc(size_t size)
+{
+    SSLModConfigRec *mc = myModConfig();
+    return ap_mm_malloc(mc->pSessionCacheDataMM, size);
+}
+
+static void *ssl_scache_shmht_calloc(size_t number, size_t size)
+{
+    SSLModConfigRec *mc = myModConfig();
+    return ap_mm_calloc(mc->pSessionCacheDataMM, number, size);
+}
+
+static void *ssl_scache_shmht_realloc(void *ptr, size_t size)
+{
+    SSLModConfigRec *mc = myModConfig();
+    return ap_mm_realloc(mc->pSessionCacheDataMM, ptr, size);
+}
+
+static void ssl_scache_shmht_free(void *ptr)
+{
+    SSLModConfigRec *mc = myModConfig();
+    ap_mm_free(mc->pSessionCacheDataMM, ptr);
+    return;
+}
+
+/*
+ * Now the actual session cache implementation
+ * based on a hash table inside a shared memory segment.
+ */
+
+void ssl_scache_shmht_init(server_rec *s, pool *p)
+{
+    SSLModConfigRec *mc = myModConfig();
+    AP_MM *mm;
+    table_t *ta;
+    int ta_errno;
+    int avail;
+    int n;
+
+    /*
+     * Create shared memory segment
+     */
+    if (mc->szSessionCacheDataFile == NULL) {
+        ssl_log(s, SSL_LOG_ERROR, "SSLSessionCache required");
+        ssl_die();
+    }
+    if ((mm = ap_mm_create(mc->nSessionCacheDataSize, 
+                           mc->szSessionCacheDataFile)) == NULL) {
+        ssl_log(s, SSL_LOG_ERROR, 
+                "Cannot allocate shared memory: %s", ap_mm_error());
+        ssl_die();
+    }
+    mc->pSessionCacheDataMM = mm;
+
+    /* 
+     * Make sure the childs have access to the underlaying files
+     */
+    ap_mm_permission(mm, SSL_MM_FILE_MODE, ap_user_id, -1);
+
+    /*
+     * Create hash table in shared memory segment
+     */
+    avail = ap_mm_available(mm);
+    n = (avail/2) / 1024;
+    n = n < 10 ? 10 : n;
+    if ((ta = table_alloc(n, &ta_errno, 
+                          ssl_scache_shmht_malloc,  
+                          ssl_scache_shmht_calloc, 
+                          ssl_scache_shmht_realloc, 
+                          ssl_scache_shmht_free    )) == NULL) {
+        ssl_log(s, SSL_LOG_ERROR,
+                "Cannot allocate hash table in shared memory: %s",
+                table_strerror(ta_errno));
+        ssl_die();
+    }
+    table_attr(ta, TABLE_FLAG_AUTO_ADJUST|TABLE_FLAG_ADJUST_DOWN);
+    table_set_data_alignment(ta, sizeof(char *));
+    table_clear(ta);
+    mc->tSessionCacheDataTable = ta;
+
+    /*
+     * Log the done work
+     */
+    ssl_log(s, SSL_LOG_INFO, 
+            "Init: Created hash-table (%d buckets) "
+            "in shared memory (%d bytes) for SSL session cache", n, avail);
+    return;
+}
+
+void ssl_scache_shmht_kill(server_rec *s)
+{
+    SSLModConfigRec *mc = myModConfig();
+
+    if (mc->pSessionCacheDataMM != NULL) {
+        ap_mm_destroy(mc->pSessionCacheDataMM);
+        mc->pSessionCacheDataMM = NULL;
+    }
+    return;
+}
+
+BOOL ssl_scache_shmht_store(server_rec *s, UCHAR *id, int idlen, time_t expiry, SSL_SESSION *sess)
+{
+    SSLModConfigRec *mc = myModConfig();
+    void *vp;
+    UCHAR ucaData[SSL_SESSION_MAX_DER];
+    int nData;
+    UCHAR *ucp;
+
+    /* streamline session data */
+    ucp = ucaData;
+    nData = i2d_SSL_SESSION(sess, &ucp);
+
+    ssl_mutex_on(s);
+    if (table_insert_kd(mc->tSessionCacheDataTable, 
+                        id, idlen, NULL, sizeof(time_t)+nData,
+                        NULL, &vp, 1) != TABLE_ERROR_NONE) {
+        ssl_mutex_off(s);
+        return FALSE;
+    }
+    memcpy(vp, &expiry, sizeof(time_t));
+    memcpy((char *)vp+sizeof(time_t), ucaData, nData);
+    ssl_mutex_off(s);
+
+    /* allow the regular expiring to occur */
+    ssl_scache_shmht_expire(s);
+
+    return TRUE;
+}
+
+SSL_SESSION *ssl_scache_shmht_retrieve(server_rec *s, UCHAR *id, int idlen)
+{
+    SSLModConfigRec *mc = myModConfig();
+    void *vp;
+    SSL_SESSION *sess = NULL;
+    UCHAR *ucpData;
+    int nData;
+    time_t expiry;
+    time_t now;
+    int n;
+
+    /* allow the regular expiring to occur */
+    ssl_scache_shmht_expire(s);
+
+    /* lookup key in table */
+    ssl_mutex_on(s);
+    if (table_retrieve(mc->tSessionCacheDataTable,
+                       id, idlen, &vp, &n) != TABLE_ERROR_NONE) {
+        ssl_mutex_off(s);
+        return NULL;
+    }
+
+    /* copy over the information to the SCI */
+    nData = n-sizeof(time_t);
+    ucpData = (UCHAR *)malloc(nData);
+    if (ucpData == NULL) {
+        ssl_mutex_off(s);
+        return NULL;
+    }
+    memcpy(&expiry, vp, sizeof(time_t));
+    memcpy(ucpData, (char *)vp+sizeof(time_t), nData);
+    ssl_mutex_off(s);
+
+    /* make sure the stuff is still not expired */
+    now = time(NULL);
+    if (expiry <= now) {
+        ssl_scache_shmht_remove(s, id, idlen);
+        return NULL;
+    }
+
+    /* unstreamed SSL_SESSION */
+    sess = d2i_SSL_SESSION(NULL, &ucpData, nData);
+
+    return sess;
+}
+
+void ssl_scache_shmht_remove(server_rec *s, UCHAR *id, int idlen)
+{
+    SSLModConfigRec *mc = myModConfig();
+
+    /* remove value under key in table */
+    ssl_mutex_on(s);
+    table_delete(mc->tSessionCacheDataTable, id, idlen, NULL, NULL);
+    ssl_mutex_off(s);
+    return;
+}
+
+void ssl_scache_shmht_expire(server_rec *s)
+{
+    SSLModConfigRec *mc = myModConfig();
+    SSLSrvConfigRec *sc = mySrvConfig(s);
+    static time_t tLast = 0;
+    table_linear_t iterator;
+    time_t tExpiresAt;
+    void *vpKey;
+    void *vpKeyThis;
+    void *vpData;
+    int nKey;
+    int nKeyThis;
+    int nData;
+    int nElements = 0;
+    int nDeleted = 0;
+    int bDelete;
+    int rc;
+    time_t tNow;
+
+    /*
+     * make sure the expiration for still not-accessed session
+     * cache entries is done only from time to time
+     */
+    tNow = time(NULL);
+    if (tNow < tLast+sc->nSessionCacheTimeout)
+        return;
+    tLast = tNow;
+
+    ssl_mutex_on(s);
+    if (table_first_r(mc->tSessionCacheDataTable, &iterator,
+                      &vpKey, &nKey, &vpData, &nData) == TABLE_ERROR_NONE) {
+        do {
+            bDelete = FALSE;
+            nElements++;
+            if (nData < sizeof(time_t) || vpData == NULL)
+                bDelete = TRUE;
+            else {
+                memcpy(&tExpiresAt, vpData, sizeof(time_t));
+                if (tExpiresAt <= tNow)
+                   bDelete = TRUE;
+            }
+            vpKeyThis = vpKey;
+            nKeyThis  = nKey;
+            rc = table_next_r(mc->tSessionCacheDataTable, &iterator,
+                              &vpKey, &nKey, &vpData, &nData);
+            if (bDelete) {
+                table_delete(mc->tSessionCacheDataTable,
+                             vpKeyThis, nKeyThis, NULL, NULL);
+                nDeleted++;
+            }
+        } while (rc == TABLE_ERROR_NONE);
+    }
+    ssl_mutex_off(s);
+    ssl_log(s, SSL_LOG_TRACE, "Inter-Process Session Cache (SHMHT) Expiry: "
+            "old: %d, new: %d, removed: %d", nElements, nElements-nDeleted, nDeleted);
+    return;
+}
+
+void ssl_scache_shmht_status(server_rec *s, pool *p, void (*func)(char *, void *), void *arg)
+{
+    SSLModConfigRec *mc = myModConfig();
+    void *vpKey;
+    void *vpData;
+    int nKey;
+    int nData;
+    int nElem;
+    int nSize;
+    int nAverage;
+
+    nElem = 0;
+    nSize = 0;
+    ssl_mutex_on(s);
+    if (table_first(mc->tSessionCacheDataTable,
+                    &vpKey, &nKey, &vpData, &nData) == TABLE_ERROR_NONE) {
+        do {
+            if (vpKey == NULL || vpData == NULL)
+                continue;
+            nElem += 1;
+            nSize += nData;
+        } while (table_next(mc->tSessionCacheDataTable,
+                            &vpKey, &nKey, &vpData, &nData) == TABLE_ERROR_NONE);
+    }
+    ssl_mutex_off(s);
+    if (nSize > 0 && nElem > 0)
+        nAverage = nSize / nElem;
+    else
+        nAverage = 0;
+    func(ap_psprintf(p, "cache type: <b>SHMHT</b>, maximum size: <b>%d</b> bytes<br>", mc->nSessionCacheDataSize), arg);
+    func(ap_psprintf(p, "current sessions: <b>%d</b>, current size: <b>%d</b> bytes<br>", nElem, nSize), arg);
+    func(ap_psprintf(p, "average session size: <b>%d</b> bytes<br>", nAverage), arg);
+    return;
+}
+
diff --git a/modules/ssl/ssl_util.c b/modules/ssl/ssl_util.c
new file mode 100644 (file)
index 0000000..af4a967
--- /dev/null
@@ -0,0 +1,437 @@
+/*                      _             _
+**  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
+** | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
+** | | | | | | (_) | (_| |   \__ \__ \ |  www.modssl.org
+** |_| |_| |_|\___/ \__,_|___|___/___/_|  ftp.modssl.org
+**                      |_____|
+**  ssl_util.c
+**  Utility Functions
+*/
+
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ *    nor may "mod_ssl" appear in their names without prior
+ *    written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+/* ====================================================================
+ * Copyright (c) 1995-1999 Ben Laurie. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by Ben Laurie
+ *    for use in the Apache-SSL HTTP server project."
+ *
+ * 4. The name "Apache-SSL Server" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 5. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Ben Laurie
+ *    for use in the Apache-SSL HTTP server project."
+ *
+ * THIS SOFTWARE IS PROVIDED BY BEN LAURIE ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL BEN LAURIE OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+                             /* ``Every day of my life
+                                  I am forced to add another
+                                  name to the list of people
+                                  who piss me off!''
+                                            -- Calvin          */
+#include "mod_ssl.h"
+
+
+/*  _________________________________________________________________
+**
+**  Utility Functions
+**  _________________________________________________________________
+*/
+
+char *ssl_util_server_root_relative(pool *p, char *what, char *arg)
+{
+    char *rv = NULL;
+
+#ifdef SSL_VENDOR
+    ap_hook_use("ap::mod_ssl::vendor::ssl_server_root_relative",
+                AP_HOOK_SIG4(ptr,ptr,ptr,ptr), AP_HOOK_ALL, &rv, p, what, arg);
+    if (rv != NULL)
+        return rv;
+#endif
+    rv = ap_server_root_relative(p, arg);
+    return rv;
+}
+
+char *ssl_util_vhostid(pool *p, server_rec *s)
+{
+    char *id;
+    SSLSrvConfigRec *sc;
+    char *host;
+    unsigned int port;
+
+    host = s->server_hostname;
+    if (s->port != 0)
+        port = s->port;
+    else {
+        sc = mySrvConfig(s);
+        if (sc->bEnabled)
+            port = DEFAULT_HTTPS_PORT;
+        else
+            port = DEFAULT_HTTP_PORT;
+    }
+    id = ap_psprintf(p, "%s:%u", host, port);
+    return id;
+}
+
+void ssl_util_strupper(char *s)
+{
+    for (; *s; ++s)
+        *s = toupper(*s);
+    return;
+}
+
+static const char ssl_util_uuencode_six2pr[64+1] =
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+void ssl_util_uuencode(char *szTo, const char *szFrom, BOOL bPad)
+{
+    ssl_util_uuencode_binary((unsigned char *)szTo,
+                             (const unsigned char *)szFrom,
+                             strlen(szFrom), bPad);
+}
+
+void ssl_util_uuencode_binary(
+    unsigned char *szTo, const unsigned char *szFrom, int nLength, BOOL bPad)
+{
+    const unsigned char *s;
+    int nPad = 0;
+
+    for (s = szFrom; nLength > 0; s += 3) {
+        *szTo++ = ssl_util_uuencode_six2pr[s[0] >> 2];
+        *szTo++ = ssl_util_uuencode_six2pr[(s[0] << 4 | s[1] >> 4) & 0x3f];
+        if (--nLength == 0) {
+            nPad = 2;
+            break;
+        }
+        *szTo++ = ssl_util_uuencode_six2pr[(s[1] << 2 | s[2] >> 6) & 0x3f];
+        if (--nLength == 0) {
+            nPad = 1;
+            break;
+        }
+        *szTo++ = ssl_util_uuencode_six2pr[s[2] & 0x3f];
+        --nLength;
+    }
+    while(bPad && nPad--)
+        *szTo++ = NUL;
+    *szTo = NUL;
+    return;
+}
+
+FILE *ssl_util_ppopen(server_rec *s, pool *p, char *cmd)
+{
+    FILE *fpout;
+    int rc;
+
+    fpout = NULL;
+    rc = ap_spawn_child(p, ssl_util_ppopen_child,
+                        (void *)cmd, kill_after_timeout,
+                        NULL, &fpout, NULL);
+    if (rc == 0 || fpout == NULL) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, s,
+                     "ssl_util_ppopen: could not run: %s", cmd);
+        return NULL;
+    }
+    return (fpout);
+}
+
+int ssl_util_ppopen_child(void *cmd, child_info *pinfo)
+{
+    int child_pid = 1;
+
+    /*
+     * Prepare for exec
+     */
+    ap_cleanup_for_exec();
+#ifdef SIGHUP
+    signal(SIGHUP, SIG_IGN);
+#endif
+
+    /*
+     * Exec() the child program
+     */
+#if defined(WIN32)
+    /* MS Windows */
+    {
+        char pCommand[MAX_STRING_LEN];
+        STARTUPINFO si;
+        PROCESS_INFORMATION pi;
+
+        ap_snprintf(pCommand, sizeof(pCommand), "%s /C %s", SHELL_PATH, cmd);
+
+        memset(&si, 0, sizeof(si));
+        memset(&pi, 0, sizeof(pi));
+
+        si.cb          = sizeof(si);
+        si.dwFlags     = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
+        si.wShowWindow = SW_HIDE;
+        si.hStdInput   = pinfo->hPipeInputRead;
+        si.hStdOutput  = pinfo->hPipeOutputWrite;
+        si.hStdError   = pinfo->hPipeErrorWrite;
+
+        if (CreateProcess(NULL, pCommand, NULL, NULL, TRUE, 0,
+                          environ, NULL, &si, &pi)) {
+            CloseHandle(pi.hProcess);
+            CloseHandle(pi.hThread);
+            child_pid = pi.dwProcessId;
+        }
+    }
+#elif defined(OS2)
+    /* IBM OS/2 */
+    spawnl(P_NOWAIT, SHELL_PATH, SHELL_PATH, "/c", (char *)cmd, NULL);
+#else
+    /* Standard Unix */
+    execl(SHELL_PATH, SHELL_PATH, "-c", (char *)cmd, NULL);
+#endif
+    return (child_pid);
+}
+
+void ssl_util_ppclose(server_rec *s, pool *p, FILE *fp)
+{
+    ap_pfclose(p, fp);
+    return;
+}
+
+/*
+ * Run a filter program and read the first line of its stdout output
+ */
+char *ssl_util_readfilter(server_rec *s, pool *p, char *cmd)
+{
+    static char buf[MAX_STRING_LEN];
+    FILE *fp;
+    char c;
+    int k;
+
+    if ((fp = ssl_util_ppopen(s, p, cmd)) == NULL)
+        return NULL;
+    for (k = 0;    read(fileno(fp), &c, 1) == 1
+                && (k < MAX_STRING_LEN-1)       ; ) {
+        if (c == '\n' || c == '\r')
+            break;
+        buf[k++] = c;
+    }
+    buf[k] = NUL;
+    ssl_util_ppclose(s, p, fp);
+
+    return buf;
+}
+
+BOOL ssl_util_path_check(ssl_pathcheck_t pcm, char *path)
+{
+    struct stat sb;
+
+    if (path == NULL)
+        return FALSE;
+    if (pcm & SSL_PCM_EXISTS && stat(path, &sb) != 0)
+        return FALSE;
+    if (pcm & SSL_PCM_ISREG && !S_ISREG(sb.st_mode))
+        return FALSE;
+    if (pcm & SSL_PCM_ISDIR && !S_ISDIR(sb.st_mode))
+        return FALSE;
+    if (pcm & SSL_PCM_ISNONZERO && sb.st_mode <= 0)
+        return FALSE;
+    return TRUE;
+}
+
+ssl_algo_t ssl_util_algotypeof(X509 *pCert, EVP_PKEY *pKey) 
+{
+    ssl_algo_t t;
+            
+    t = SSL_ALGO_UNKNOWN;
+    if (pCert != NULL)
+        pKey = X509_get_pubkey(pCert);
+    if (pKey != NULL) {
+        switch (EVP_PKEY_type(pKey->type)) {
+            case EVP_PKEY_RSA: 
+                t = SSL_ALGO_RSA;
+                break;
+            case EVP_PKEY_DSA: 
+                t = SSL_ALGO_DSA;
+                break;
+            default:
+                break;
+        }
+    }
+    return t;
+}
+
+char *ssl_util_algotypestr(ssl_algo_t t) 
+{
+    char *cp;
+
+    cp = "UNKNOWN";
+    switch (t) {
+        case SSL_ALGO_RSA: 
+            cp = "RSA";
+            break;
+        case SSL_ALGO_DSA: 
+            cp = "DSA";
+            break;
+        default:
+            break;
+    }
+    return cp;
+}
+
+char *ssl_util_ptxtsub(
+    pool *p, const char *cpLine, const char *cpMatch, char *cpSubst)
+{
+#define MAX_PTXTSUB 100
+    char *cppMatch[MAX_PTXTSUB];
+    char *cpResult;
+    int nResult;
+    int nLine;
+    int nSubst;
+    int nMatch;
+    char *cpI;
+    char *cpO;
+    char *cp;
+    int i;
+
+    /*
+     * Pass 1: find substitution locations and calculate sizes
+     */
+    nLine  = strlen(cpLine);
+    nMatch = strlen(cpMatch);
+    nSubst = strlen(cpSubst);
+    for (cpI = (char *)cpLine, i = 0, nResult = 0;
+         cpI < cpLine+nLine && i < MAX_PTXTSUB;    ) {
+        if ((cp = strstr(cpI, cpMatch)) != NULL) {
+            cppMatch[i++] = cp;
+            nResult += ((cp-cpI)+nSubst);
+            cpI = (cp+nMatch);
+        }
+        else {
+            nResult += strlen(cpI);
+            break;
+        }
+    }
+    cppMatch[i] = NULL;
+    if (i == 0)
+        return NULL;
+
+    /*
+     * Pass 2: allocate memory and assemble result
+     */
+    cpResult = ap_pcalloc(p, nResult+1);
+    for (cpI = (char *)cpLine, cpO = cpResult, i = 0; cppMatch[i] != NULL; i++) {
+        ap_cpystrn(cpO, cpI, cppMatch[i]-cpI+1);
+        cpO += (cppMatch[i]-cpI);
+        ap_cpystrn(cpO, cpSubst, nSubst+1);
+        cpO += nSubst;
+        cpI = (cppMatch[i]+nMatch);
+    }
+    ap_cpystrn(cpO, cpI, cpResult+nResult-cpO+1);
+
+    return cpResult;
+}
+
+/*  _________________________________________________________________
+**
+**  Special Functions for Win32/OpenSSL
+**  _________________________________________________________________
+*/
+
+#ifdef WIN32
+static HANDLE lock_cs[CRYPTO_NUM_LOCKS];
+
+static void win32_locking_callback(int mode, int type, char* file, int line)
+{
+    if (mode & CRYPTO_LOCK)
+        WaitForSingleObject(lock_cs[type], INFINITE);
+    else
+        ReleaseMutex(lock_cs[type]);
+    return;
+}
+#endif /* WIN32 */
+
+void ssl_util_thread_setup(void)
+{
+#ifdef WIN32
+    int i;
+
+    for (i = 0; i < CRYPTO_NUM_LOCKS; i++)
+        lock_cs[i] = CreateMutex(NULL, FALSE, NULL);
+    CRYPTO_set_locking_callback((void(*)(int, int, const char *, int))
+                                win32_locking_callback);
+#endif /* WIN32 */
+    return;
+}
+
diff --git a/modules/ssl/ssl_util_sdbm.c b/modules/ssl/ssl_util_sdbm.c
new file mode 100644 (file)
index 0000000..c728f90
--- /dev/null
@@ -0,0 +1,926 @@
+/*                      _             _
+**  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
+** | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
+** | | | | | | (_) | (_| |   \__ \__ \ |  www.modssl.org
+** |_| |_| |_|\___/ \__,_|___|___/___/_|  ftp.modssl.org
+**                      |_____|
+**  ssl_util_sdbm.c
+**  Built-in Simple DBM
+*/
+
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ *    nor may "mod_ssl" appear in their names without prior
+ *    written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+/*
+ * sdbm - ndbm work-alike hashed database library
+ * based on Per-Aake Larson's Dynamic Hashing algorithms. BIT 18 (1978).
+ * author: oz@nexus.yorku.ca
+ * status: public domain.
+ *
+ * core routines
+ */
+
+#include "mod_ssl.h"
+
+#ifdef SSL_USE_SDBM
+
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef WIN32
+#include <io.h>
+#include <errno.h>
+#else
+#include <unistd.h>
+#endif
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#ifdef __STDC__
+#include <stddef.h>
+#endif
+
+#ifndef NULL
+#define NULL (void *)0
+#endif
+
+/*
+ * externals
+ */
+#ifdef sun
+extern int errno;
+#endif
+
+/*
+ * forward
+ */
+static int getdbit proto((DBM *, long));
+static int setdbit proto((DBM *, long));
+static int getpage proto((DBM *, long));
+static datum getnext proto((DBM *));
+static int makroom proto((DBM *, long, int));
+
+/*
+ * useful macros
+ */
+#define bad(x)          ((x).dptr == NULL || (x).dsize <= 0)
+#define exhash(item)    sdbm_hash((item).dptr, (item).dsize)
+#define ioerr(db)       ((db)->flags |= DBM_IOERR)
+
+#define OFF_PAG(off)    (long) (off) * PBLKSIZ
+#define OFF_DIR(off)    (long) (off) * DBLKSIZ
+
+static long masks[] = {
+        000000000000, 000000000001, 000000000003, 000000000007,
+        000000000017, 000000000037, 000000000077, 000000000177,
+        000000000377, 000000000777, 000000001777, 000000003777,
+        000000007777, 000000017777, 000000037777, 000000077777,
+        000000177777, 000000377777, 000000777777, 000001777777,
+        000003777777, 000007777777, 000017777777, 000037777777,
+        000077777777, 000177777777, 000377777777, 000777777777,
+        001777777777, 003777777777, 007777777777, 017777777777
+};
+
+datum nullitem = {NULL, 0};
+
+DBM *
+sdbm_open(file, flags, mode)
+register char *file;
+register int flags;
+register int mode;
+{
+        register DBM *db;
+        register char *dirname;
+        register char *pagname;
+        register int n;
+
+        if (file == NULL || !*file)
+                return errno = EINVAL, (DBM *) NULL;
+/*
+ * need space for two seperate filenames
+ */
+        n = strlen(file) * 2 + strlen(DIRFEXT) + strlen(PAGFEXT) + 2;
+
+        if ((dirname = malloc((unsigned) n)) == NULL)
+                return errno = ENOMEM, (DBM *) NULL;
+/*
+ * build the file names
+ */
+        dirname = strcat(strcpy(dirname, file), DIRFEXT);
+        pagname = strcpy(dirname + strlen(dirname) + 1, file);
+        pagname = strcat(pagname, PAGFEXT);
+
+        db = sdbm_prep(dirname, pagname, flags, mode);
+        free((char *) dirname);
+        return db;
+}
+
+DBM *
+sdbm_prep(dirname, pagname, flags, mode)
+char *dirname;
+char *pagname;
+int flags;
+int mode;
+{
+        register DBM *db;
+        struct stat dstat;
+
+        if ((db = (DBM *) malloc(sizeof(DBM))) == NULL)
+                return errno = ENOMEM, (DBM *) NULL;
+
+        db->flags = 0;
+        db->hmask = 0;
+        db->blkptr = 0;
+        db->keyptr = 0;
+/*
+ * adjust user flags so that WRONLY becomes RDWR,
+ * as required by this package. Also set our internal
+ * flag for RDONLY if needed.
+ */
+        if (flags & O_WRONLY)
+                flags = (flags & ~O_WRONLY) | O_RDWR;
+        else if ((flags & 03) == O_RDONLY)
+                db->flags = DBM_RDONLY;
+#if defined(OS2) || defined(MSDOS) || defined(WIN32)
+        flags |= O_BINARY;
+#endif
+
+/*
+ * open the files in sequence, and stat the dirfile.
+ * If we fail anywhere, undo everything, return NULL.
+ */
+        if ((db->pagf = open(pagname, flags, mode)) > -1) {
+                if ((db->dirf = open(dirname, flags, mode)) > -1) {
+/*
+ * need the dirfile size to establish max bit number.
+ */
+                        if (fstat(db->dirf, &dstat) == 0) {
+/*
+ * zero size: either a fresh database, or one with a single,
+ * unsplit data page: dirpage is all zeros.
+ */
+                                db->dirbno = (!dstat.st_size) ? 0 : -1;
+                                db->pagbno = -1;
+                                db->maxbno = dstat.st_size * BYTESIZ;
+
+                                (void) memset(db->pagbuf, 0, PBLKSIZ);
+                                (void) memset(db->dirbuf, 0, DBLKSIZ);
+                        /*
+                         * success
+                         */
+                                return db;
+                        }
+                        (void) close(db->dirf);
+                }
+                (void) close(db->pagf);
+        }
+        free((char *) db);
+        return (DBM *) NULL;
+}
+
+void
+sdbm_close(db)
+register DBM *db;
+{
+        if (db == NULL)
+                errno = EINVAL;
+        else {
+                (void) close(db->dirf);
+                (void) close(db->pagf);
+                free((char *) db);
+        }
+}
+
+datum
+sdbm_fetch(db, key)
+register DBM *db;
+datum key;
+{
+        if (db == NULL || bad(key))
+                return errno = EINVAL, nullitem;
+
+        if (getpage(db, exhash(key)))
+                return getpair(db->pagbuf, key);
+
+        return ioerr(db), nullitem;
+}
+
+int
+sdbm_delete(db, key)
+register DBM *db;
+datum key;
+{
+        if (db == NULL || bad(key))
+                return errno = EINVAL, -1;
+        if (sdbm_rdonly(db))
+                return errno = EPERM, -1;
+
+        if (getpage(db, exhash(key))) {
+                if (!delpair(db->pagbuf, key))
+                        return -1;
+/*
+ * update the page file
+ */
+                if (lseek(db->pagf, OFF_PAG(db->pagbno), SEEK_SET) < 0
+                    || write(db->pagf, db->pagbuf, PBLKSIZ) < 0)
+                        return ioerr(db), -1;
+
+                return 0;
+        }
+
+        return ioerr(db), -1;
+}
+
+int
+sdbm_store(db, key, val, flags)
+register DBM *db;
+datum key;
+datum val;
+int flags;
+{
+        int need;
+        register long hash;
+
+        if (db == NULL || bad(key))
+                return errno = EINVAL, -1;
+        if (sdbm_rdonly(db))
+                return errno = EPERM, -1;
+
+        need = key.dsize + val.dsize;
+/*
+ * is the pair too big (or too small) for this database ??
+ */
+        if (need < 0 || need > PAIRMAX)
+                return errno = EINVAL, -1;
+
+        if (getpage(db, (hash = exhash(key)))) {
+/*
+ * if we need to replace, delete the key/data pair
+ * first. If it is not there, ignore.
+ */
+                if (flags == DBM_REPLACE)
+                        (void) delpair(db->pagbuf, key);
+#ifdef SEEDUPS
+                else if (duppair(db->pagbuf, key))
+                        return 1;
+#endif
+/*
+ * if we do not have enough room, we have to split.
+ */
+                if (!fitpair(db->pagbuf, need))
+                        if (!makroom(db, hash, need))
+                                return ioerr(db), -1;
+/*
+ * we have enough room or split is successful. insert the key,
+ * and update the page file.
+ */
+                (void) putpair(db->pagbuf, key, val);
+
+                if (lseek(db->pagf, OFF_PAG(db->pagbno), SEEK_SET) < 0
+                    || write(db->pagf, db->pagbuf, PBLKSIZ) < 0)
+                        return ioerr(db), -1;
+        /*
+         * success
+         */
+                return 0;
+        }
+
+        return ioerr(db), -1;
+}
+
+/*
+ * makroom - make room by splitting the overfull page
+ * this routine will attempt to make room for SPLTMAX times before
+ * giving up.
+ */
+static int
+makroom(db, hash, need)
+register DBM *db;
+long hash;
+int need;
+{
+        long newp;
+        char twin[PBLKSIZ];
+        char *pag = db->pagbuf;
+        char *new = twin;
+        register int smax = SPLTMAX;
+
+        do {
+/*
+ * split the current page
+ */
+                (void) splpage(pag, new, db->hmask + 1);
+/*
+ * address of the new page
+ */
+                newp = (hash & db->hmask) | (db->hmask + 1);
+
+/*
+ * write delay, read avoidence/cache shuffle:
+ * select the page for incoming pair: if key is to go to the new page,
+ * write out the previous one, and copy the new one over, thus making
+ * it the current page. If not, simply write the new page, and we are
+ * still looking at the page of interest. current page is not updated
+ * here, as sdbm_store will do so, after it inserts the incoming pair.
+ */
+                if (hash & (db->hmask + 1)) {
+                        if (lseek(db->pagf, OFF_PAG(db->pagbno), SEEK_SET) < 0
+                            || write(db->pagf, db->pagbuf, PBLKSIZ) < 0)
+                                return 0;
+                        db->pagbno = newp;
+                        (void) memcpy(pag, new, PBLKSIZ);
+                }
+                else if (lseek(db->pagf, OFF_PAG(newp), SEEK_SET) < 0
+                         || write(db->pagf, new, PBLKSIZ) < 0)
+                        return 0;
+
+                if (!setdbit(db, db->curbit))
+                        return 0;
+/*
+ * see if we have enough room now
+ */
+                if (fitpair(pag, need))
+                        return 1;
+/*
+ * try again... update curbit and hmask as getpage would have
+ * done. because of our update of the current page, we do not
+ * need to read in anything. BUT we have to write the current
+ * [deferred] page out, as the window of failure is too great.
+ */
+                db->curbit = 2 * db->curbit +
+                        ((hash & (db->hmask + 1)) ? 2 : 1);
+                db->hmask |= db->hmask + 1;
+
+                if (lseek(db->pagf, OFF_PAG(db->pagbno), SEEK_SET) < 0
+                    || write(db->pagf, db->pagbuf, PBLKSIZ) < 0)
+                        return 0;
+
+        } while (--smax);
+/*
+ * if we are here, this is real bad news. After SPLTMAX splits,
+ * we still cannot fit the key. say goodnight.
+ */
+#ifdef BADMESS
+        (void) write(2, "sdbm: cannot insert after SPLTMAX attempts.\n", 44);
+#endif
+        return 0;
+
+}
+
+/*
+ * the following two routines will break if
+ * deletions aren't taken into account. (ndbm bug)
+ */
+datum
+sdbm_firstkey(db)
+register DBM *db;
+{
+        if (db == NULL)
+                return errno = EINVAL, nullitem;
+/*
+ * start at page 0
+ */
+        if (lseek(db->pagf, OFF_PAG(0), SEEK_SET) < 0
+            || read(db->pagf, db->pagbuf, PBLKSIZ) < 0)
+                return ioerr(db), nullitem;
+        db->pagbno = 0;
+        db->blkptr = 0;
+        db->keyptr = 0;
+
+        return getnext(db);
+}
+
+datum
+sdbm_nextkey(db)
+register DBM *db;
+{
+        if (db == NULL)
+                return errno = EINVAL, nullitem;
+        return getnext(db);
+}
+
+/*
+ * all important binary trie traversal
+ */
+static int
+getpage(db, hash)
+register DBM *db;
+register long hash;
+{
+        register int hbit;
+        register long dbit;
+        register long pagb;
+
+        dbit = 0;
+        hbit = 0;
+        while (dbit < db->maxbno && getdbit(db, dbit))
+                dbit = 2 * dbit + ((hash & (1 << hbit++)) ? 2 : 1);
+
+        debug(("dbit: %d...", dbit));
+
+        db->curbit = dbit;
+        db->hmask = masks[hbit];
+
+        pagb = hash & db->hmask;
+/*
+ * see if the block we need is already in memory.
+ * note: this lookaside cache has about 10% hit rate.
+ */
+        if (pagb != db->pagbno) {
+/*
+ * note: here, we assume a "hole" is read as 0s.
+ * if not, must zero pagbuf first.
+ */
+                if (lseek(db->pagf, OFF_PAG(pagb), SEEK_SET) < 0
+                    || read(db->pagf, db->pagbuf, PBLKSIZ) < 0)
+                        return 0;
+                if (!chkpage(db->pagbuf))
+                        return 0;
+                db->pagbno = pagb;
+
+                debug(("pag read: %d\n", pagb));
+        }
+        return 1;
+}
+
+static int
+getdbit(db, dbit)
+register DBM *db;
+register long dbit;
+{
+        register long c;
+        register long dirb;
+
+        c = dbit / BYTESIZ;
+        dirb = c / DBLKSIZ;
+
+        if (dirb != db->dirbno) {
+                if (lseek(db->dirf, OFF_DIR(dirb), SEEK_SET) < 0
+                    || read(db->dirf, db->dirbuf, DBLKSIZ) < 0)
+                        return 0;
+                db->dirbno = dirb;
+
+                debug(("dir read: %d\n", dirb));
+        }
+
+        return db->dirbuf[c % DBLKSIZ] & (1 << dbit % BYTESIZ);
+}
+
+static int
+setdbit(db, dbit)
+register DBM *db;
+register long dbit;
+{
+        register long c;
+        register long dirb;
+
+        c = dbit / BYTESIZ;
+        dirb = c / DBLKSIZ;
+
+        if (dirb != db->dirbno) {
+                if (lseek(db->dirf, OFF_DIR(dirb), SEEK_SET) < 0
+                    || read(db->dirf, db->dirbuf, DBLKSIZ) < 0)
+                        return 0;
+                db->dirbno = dirb;
+
+                debug(("dir read: %d\n", dirb));
+        }
+
+        db->dirbuf[c % DBLKSIZ] |= (1 << dbit % BYTESIZ);
+
+        if (dbit >= db->maxbno)
+                db->maxbno += DBLKSIZ * BYTESIZ;
+
+        if (lseek(db->dirf, OFF_DIR(dirb), SEEK_SET) < 0
+            || write(db->dirf, db->dirbuf, DBLKSIZ) < 0)
+                return 0;
+
+        return 1;
+}
+
+/*
+ * getnext - get the next key in the page, and if done with
+ * the page, try the next page in sequence
+ */
+static datum
+getnext(db)
+register DBM *db;
+{
+        datum key;
+
+        for (;;) {
+                db->keyptr++;
+                key = getnkey(db->pagbuf, db->keyptr);
+                if (key.dptr != NULL)
+                        return key;
+/*
+ * we either run out, or there is nothing on this page..
+ * try the next one... If we lost our position on the
+ * file, we will have to seek.
+ */
+                db->keyptr = 0;
+                if (db->pagbno != db->blkptr++)
+                        if (lseek(db->pagf, OFF_PAG(db->blkptr), SEEK_SET) < 0)
+                                break;
+                db->pagbno = db->blkptr;
+                if (read(db->pagf, db->pagbuf, PBLKSIZ) <= 0)
+                        break;
+                if (!chkpage(db->pagbuf))
+                        break;
+        }
+
+        return ioerr(db), nullitem;
+}
+
+/* ************************* */
+
+/*
+ * sdbm - ndbm work-alike hashed database library
+ * based on Per-Aake Larson's Dynamic Hashing algorithms. BIT 18 (1978).
+ * author: oz@nexus.yorku.ca
+ * status: public domain. keep it that way.
+ *
+ * hashing routine
+ */
+
+/*
+ * polynomial conversion ignoring overflows
+ * [this seems to work remarkably well, in fact better
+ * then the ndbm hash function. Replace at your own risk]
+ * use: 65599   nice.
+ *      65587   even better.
+ */
+long
+sdbm_hash(str, len)
+register char *str;
+register int len;
+{
+        register unsigned long n = 0;
+
+#ifdef DUFF
+#define HASHC   n = *str++ + 65599 * n
+        if (len > 0) {
+                register int loop = (len + 8 - 1) >> 3;
+
+                switch(len & (8 - 1)) {
+                case 0: do {
+                        HASHC;  case 7: HASHC;
+                case 6: HASHC;  case 5: HASHC;
+                case 4: HASHC;  case 3: HASHC;
+                case 2: HASHC;  case 1: HASHC;
+                        } while (--loop);
+                }
+
+        }
+#else
+        while (len--)
+                n = *str++ + 65599 * n;
+#endif
+        return n;
+}
+
+/* ************************* */
+
+/*
+ * sdbm - ndbm work-alike hashed database library
+ * based on Per-Aake Larson's Dynamic Hashing algorithms. BIT 18 (1978).
+ * author: oz@nexus.yorku.ca
+ * status: public domain.
+ *
+ * page-level routines
+ */
+
+#define exhash(item)    sdbm_hash((item).dptr, (item).dsize)
+
+/*
+ * forward
+ */
+static int seepair proto((char *, int, char *, int));
+
+/*
+ * page format:
+ *      +------------------------------+
+ * ino  | n | keyoff | datoff | keyoff |
+ *      +------------+--------+--------+
+ *      | datoff | - - - ---->         |
+ *      +--------+---------------------+
+ *      |        F R E E A R E A       |
+ *      +--------------+---------------+
+ *      |  <---- - - - | data          |
+ *      +--------+-----+----+----------+
+ *      |  key   | data     | key      |
+ *      +--------+----------+----------+
+ *
+ * calculating the offsets for free area:  if the number
+ * of entries (ino[0]) is zero, the offset to the END of
+ * the free area is the block size. Otherwise, it is the
+ * nth (ino[ino[0]]) entry's offset.
+ */
+
+int
+fitpair(pag, need)
+char *pag;
+int need;
+{
+        register int n;
+        register int off;
+        register int avail;
+        register short *ino = (short *) pag;
+
+        off = ((n = ino[0]) > 0) ? ino[n] : PBLKSIZ;
+        avail = off - (n + 1) * sizeof(short);
+        need += 2 * sizeof(short);
+
+        debug(("free %d need %d\n", avail, need));
+
+        return need <= avail;
+}
+
+void
+putpair(pag, key, val)
+char *pag;
+datum key;
+datum val;
+{
+        register int n;
+        register int off;
+        register short *ino = (short *) pag;
+
+        off = ((n = ino[0]) > 0) ? ino[n] : PBLKSIZ;
+/*
+ * enter the key first
+ */
+        off -= key.dsize;
+        (void) memcpy(pag + off, key.dptr, key.dsize);
+        ino[n + 1] = off;
+/*
+ * now the data
+ */
+        off -= val.dsize;
+        (void) memcpy(pag + off, val.dptr, val.dsize);
+        ino[n + 2] = off;
+/*
+ * adjust item count
+ */
+        ino[0] += 2;
+}
+
+datum
+getpair(pag, key)
+char *pag;
+datum key;
+{
+        register int i;
+        register int n;
+        datum val;
+        register short *ino = (short *) pag;
+
+        if ((n = ino[0]) == 0)
+                return nullitem;
+
+        if ((i = seepair(pag, n, key.dptr, key.dsize)) == 0)
+                return nullitem;
+
+        val.dptr = pag + ino[i + 1];
+        val.dsize = ino[i] - ino[i + 1];
+        return val;
+}
+
+#ifdef SEEDUPS
+int
+duppair(pag, key)
+char *pag;
+datum key;
+{
+        register short *ino = (short *) pag;
+        return ino[0] > 0 && seepair(pag, ino[0], key.dptr, key.dsize) > 0;
+}
+#endif
+
+datum
+getnkey(pag, num)
+char *pag;
+int num;
+{
+        datum key;
+        register int off;
+        register short *ino = (short *) pag;
+
+        num = num * 2 - 1;
+        if (ino[0] == 0 || num > ino[0])
+                return nullitem;
+
+        off = (num > 1) ? ino[num - 1] : PBLKSIZ;
+
+        key.dptr = pag + ino[num];
+        key.dsize = off - ino[num];
+
+        return key;
+}
+
+int
+delpair(pag, key)
+char *pag;
+datum key;
+{
+        register int n;
+        register int i;
+        register short *ino = (short *) pag;
+
+        if ((n = ino[0]) == 0)
+                return 0;
+
+        if ((i = seepair(pag, n, key.dptr, key.dsize)) == 0)
+                return 0;
+/*
+ * found the key. if it is the last entry
+ * [i.e. i == n - 1] we just adjust the entry count.
+ * hard case: move all data down onto the deleted pair,
+ * shift offsets onto deleted offsets, and adjust them.
+ * [note: 0 < i < n]
+ */
+        if (i < n - 1) {
+                register int m;
+                register char *dst = pag + (i == 1 ? PBLKSIZ : ino[i - 1]);
+                register char *src = pag + ino[i + 1];
+                register int   zoo = dst - src;
+
+                debug(("free-up %d ", zoo));
+/*
+ * shift data/keys down
+ */
+                m = ino[i + 1] - ino[n];
+#ifdef DUFF
+#define MOVB    *--dst = *--src
+                if (m > 0) {
+                        register int loop = (m + 8 - 1) >> 3;
+
+                        switch (m & (8 - 1)) {
+                        case 0: do {
+                                MOVB;   case 7: MOVB;
+                        case 6: MOVB;   case 5: MOVB;
+                        case 4: MOVB;   case 3: MOVB;
+                        case 2: MOVB;   case 1: MOVB;
+                                } while (--loop);
+                        }
+                }
+#else
+                dst -= m;
+                src -= m;
+                memmove(dst, src, m);
+#endif
+/*
+ * adjust offset index up
+ */
+                while (i < n - 1) {
+                        ino[i] = ino[i + 2] + zoo;
+                        i++;
+                }
+        }
+        ino[0] -= 2;
+        return 1;
+}
+
+/*
+ * search for the key in the page.
+ * return offset index in the range 0 < i < n.
+ * return 0 if not found.
+ */
+static int
+seepair(pag, n, key, siz)
+char *pag;
+register int n;
+register char *key;
+register int siz;
+{
+        register int i;
+        register int off = PBLKSIZ;
+        register short *ino = (short *) pag;
+
+        for (i = 1; i < n; i += 2) {
+                if (siz == off - ino[i] &&
+                    memcmp(key, pag + ino[i], siz) == 0)
+                        return i;
+                off = ino[i + 1];
+        }
+        return 0;
+}
+
+void
+splpage(pag, new, sbit)
+char *pag;
+char *new;
+long sbit;
+{
+        datum key;
+        datum val;
+
+        register int n;
+        register int off = PBLKSIZ;
+        char cur[PBLKSIZ];
+        register short *ino = (short *) cur;
+
+        (void) memcpy(cur, pag, PBLKSIZ);
+        (void) memset(pag, 0, PBLKSIZ);
+        (void) memset(new, 0, PBLKSIZ);
+
+        n = ino[0];
+        for (ino++; n > 0; ino += 2) {
+                key.dptr = cur + ino[0];
+                key.dsize = off - ino[0];
+                val.dptr = cur + ino[1];
+                val.dsize = ino[0] - ino[1];
+/*
+ * select the page pointer (by looking at sbit) and insert
+ */
+                (void) putpair((exhash(key) & sbit) ? new : pag, key, val);
+
+                off = ino[1];
+                n -= 2;
+        }
+
+        debug(("%d split %d/%d\n", ((short *) cur)[0] / 2,
+               ((short *) new)[0] / 2,
+               ((short *) pag)[0] / 2));
+}
+
+/*
+ * check page sanity:
+ * number of entries should be something
+ * reasonable, and all offsets in the index should be in order.
+ * this could be made more rigorous.
+ */
+int
+chkpage(pag)
+char *pag;
+{
+        register int n;
+        register int off;
+        register short *ino = (short *) pag;
+
+        if ((n = ino[0]) < 0 || n > PBLKSIZ / sizeof(short))
+                return 0;
+
+        if (n > 0) {
+                off = PBLKSIZ;
+                for (ino++; n > 0; ino += 2) {
+                        if (ino[0] > off || ino[1] > off ||
+                            ino[1] > ino[0])
+                                return 0;
+                        off = ino[1];
+                        n -= 2;
+                }
+        }
+        return 1;
+}
+
+#endif /* SSL_USE_SDBM */
diff --git a/modules/ssl/ssl_util_sdbm.h b/modules/ssl/ssl_util_sdbm.h
new file mode 100644 (file)
index 0000000..723e809
--- /dev/null
@@ -0,0 +1,191 @@
+/*                      _             _
+**  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
+** | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
+** | | | | | | (_) | (_| |   \__ \__ \ |  www.modssl.org
+** |_| |_| |_|\___/ \__,_|___|___/___/_|  ftp.modssl.org
+**                      |_____|
+**  ssl_util_sdbm.c
+**  Built-in Simple DBM (Header)
+*/
+
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ *    nor may "mod_ssl" appear in their names without prior
+ *    written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+/*
+ * sdbm - ndbm work-alike hashed database library
+ * based on Per-Ake Larson's Dynamic Hashing algorithms. BIT 18 (1978).
+ * author: oz@nexus.yorku.ca
+ * status: public domain.
+ */
+
+#ifndef SSL_UTIL_SDBM_H
+#define SSL_UTIL_SDBM_H
+
+#define DUFF    /* go ahead and use the loop-unrolled version */
+
+#include <stdio.h>
+
+#ifdef MOD_SSL
+#define DBLKSIZ 16384                   /* SSL cert chains require more */
+#define PBLKSIZ 8192                    /* SSL cert chains require more */
+#define PAIRMAX 8008                    /* arbitrary on PBLKSIZ-N */
+#else
+#define DBLKSIZ 4096
+#define PBLKSIZ 1024
+#define PAIRMAX 1008                    /* arbitrary on PBLKSIZ-N */
+#endif
+#define SPLTMAX 10                      /* maximum allowed splits */
+                                        /* for a single insertion */
+#define DIRFEXT ".dir"
+#define PAGFEXT ".pag"
+
+typedef struct {
+        int dirf;                      /* directory file descriptor */
+        int pagf;                      /* page file descriptor */
+        int flags;                     /* status/error flags, see below */
+        long maxbno;                   /* size of dirfile in bits */
+        long curbit;                   /* current bit number */
+        long hmask;                    /* current hash mask */
+        long blkptr;                   /* current block for nextkey */
+        int keyptr;                    /* current key for nextkey */
+        long blkno;                    /* current page to read/write */
+        long pagbno;                   /* current page in pagbuf */
+        char pagbuf[PBLKSIZ];          /* page file block buffer */
+        long dirbno;                   /* current block in dirbuf */
+        char dirbuf[DBLKSIZ];          /* directory file block buffer */
+} DBM;
+
+#define DBM_RDONLY      0x1            /* data base open read-only */
+#define DBM_IOERR       0x2            /* data base I/O error */
+
+/*
+ * utility macros
+ */
+#define sdbm_rdonly(db)         ((db)->flags & DBM_RDONLY)
+#define sdbm_error(db)          ((db)->flags & DBM_IOERR)
+
+#define sdbm_clearerr(db)       ((db)->flags &= ~DBM_IOERR)  /* ouch */
+
+#define sdbm_dirfno(db) ((db)->dirf)
+#define sdbm_pagfno(db) ((db)->pagf)
+
+typedef struct {
+        char *dptr;
+        int dsize;
+} datum;
+
+extern datum nullitem;
+
+#ifdef __STDC__
+#define proto(p) p
+#else
+#define proto(p) ()
+#endif
+
+/*
+ * flags to sdbm_store
+ */
+#define DBM_INSERT      0
+#define DBM_REPLACE     1
+
+/*
+ * ndbm interface
+ */
+extern DBM *sdbm_open proto((char *, int, int));
+extern void sdbm_close proto((DBM *));
+extern datum sdbm_fetch proto((DBM *, datum));
+extern int sdbm_delete proto((DBM *, datum));
+extern int sdbm_store proto((DBM *, datum, datum, int));
+extern datum sdbm_firstkey proto((DBM *));
+extern datum sdbm_nextkey proto((DBM *));
+
+/*
+ * other
+ */
+extern DBM *sdbm_prep proto((char *, char *, int, int));
+extern long sdbm_hash proto((char *, int));
+
+/* pair.h */
+extern int fitpair proto((char *, int));
+extern void  putpair proto((char *, datum, datum));
+extern datum    getpair proto((char *, datum));
+extern int  delpair proto((char *, datum));
+extern int  chkpage proto((char *));
+extern datum getnkey proto((char *, int));
+extern void splpage proto((char *, char *, long));
+extern int duppair proto((char *, datum));
+
+/* tune.h */
+/*
+ * sdbm - ndbm work-alike hashed database library
+ * tuning and portability constructs [not nearly enough]
+ * author: oz@nexus.yorku.ca
+ */
+
+#define BYTESIZ         8
+
+/*
+ * important tuning parms (hah)
+ */
+
+#define SEEDUPS                 /* always detect duplicates */
+#define BADMESS                 /* generate a message for worst case:
+                                   cannot make room after SPLTMAX splits */
+/*
+ * misc
+ */
+#ifdef DEBUG
+#define debug(x)        printf x
+#else
+#define debug(x)
+#endif
+
+#endif /* SSL_UTIL_SDBM_H */
diff --git a/modules/ssl/ssl_util_ssl.c b/modules/ssl/ssl_util_ssl.c
new file mode 100644 (file)
index 0000000..19f6bd3
--- /dev/null
@@ -0,0 +1,544 @@
+/*                      _             _
+**  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
+** | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
+** | | | | | | (_) | (_| |   \__ \__ \ |  www.modssl.org
+** |_| |_| |_|\___/ \__,_|___|___/___/_|  ftp.modssl.org
+**                      |_____|
+**  ssl_util_ssl.c
+**  Additional Utility Functions for OpenSSL
+*/
+
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ *    nor may "mod_ssl" appear in their names without prior
+ *    written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include "mod_ssl.h"
+
+
+/*  _________________________________________________________________
+**
+**  Additional High-Level Functions for OpenSSL
+**  _________________________________________________________________
+*/
+
+int SSL_get_app_data2_idx(void)
+{
+   static int app_data2_idx = -1;
+
+   if (app_data2_idx < 0) {
+      app_data2_idx = SSL_get_ex_new_index(0,
+           "Second Application Data for SSL", NULL, NULL, NULL);
+      app_data2_idx = SSL_get_ex_new_index(0,
+           "Second Application Data for SSL", NULL, NULL, NULL);
+   }
+   return(app_data2_idx);
+}
+
+void *SSL_get_app_data2(SSL *ssl)
+{
+    return (void *)SSL_get_ex_data(ssl, SSL_get_app_data2_idx());
+}
+
+void SSL_set_app_data2(SSL *ssl, void *arg)
+{
+    SSL_set_ex_data(ssl, SSL_get_app_data2_idx(), (char *)arg);
+    return;
+}
+
+/*  _________________________________________________________________
+**
+**  High-Level Certificate / Private Key Loading
+**  _________________________________________________________________
+*/
+
+X509 *SSL_read_X509(FILE *fp, X509 **x509, int (*cb)())
+{
+    X509 *rc;
+    BIO *bioS;
+    BIO *bioF;
+
+    /* 1. try PEM (= DER+Base64+headers) */
+#if SSL_LIBRARY_VERSION < 0x00904000
+    rc = PEM_read_X509(fp, x509, cb);
+#else
+    rc = PEM_read_X509(fp, x509, cb, NULL);
+#endif
+    if (rc == NULL) {
+        /* 2. try DER+Base64 */
+        fseek(fp, 0L, SEEK_SET);
+        if ((bioS = BIO_new(BIO_s_fd())) == NULL)
+            return NULL;
+        BIO_set_fd(bioS, fileno(fp), BIO_NOCLOSE);
+        if ((bioF = BIO_new(BIO_f_base64())) == NULL) {
+            BIO_free(bioS);
+            return NULL;
+        }
+        bioS = BIO_push(bioF, bioS);
+        rc = d2i_X509_bio(bioS, NULL);
+        BIO_free_all(bioS);
+        if (rc == NULL) {
+            /* 3. try plain DER */
+            fseek(fp, 0L, SEEK_SET);
+            if ((bioS = BIO_new(BIO_s_fd())) == NULL)
+                return NULL;
+            BIO_set_fd(bioS, fileno(fp), BIO_NOCLOSE);
+            rc = d2i_X509_bio(bioS, NULL);
+            BIO_free(bioS);
+        }
+    }
+    if (rc != NULL && x509 != NULL) {
+        if (*x509 != NULL)
+            X509_free(*x509);
+        *x509 = rc;
+    }
+    return rc;
+}
+
+#if SSL_LIBRARY_VERSION <= 0x00904100
+static EVP_PKEY *d2i_PrivateKey_bio(BIO *bio, EVP_PKEY **key)
+{
+     return ((EVP_PKEY *)ASN1_d2i_bio(
+             (char *(*)())EVP_PKEY_new, 
+             (char *(*)())d2i_PrivateKey, 
+             (bio), (unsigned char **)(key)));
+}
+#endif
+
+EVP_PKEY *SSL_read_PrivateKey(FILE *fp, EVP_PKEY **key, int (*cb)())
+{
+    EVP_PKEY *rc;
+    BIO *bioS;
+    BIO *bioF;
+
+    /* 1. try PEM (= DER+Base64+headers) */
+#if SSL_LIBRARY_VERSION < 0x00904000
+    rc = PEM_read_PrivateKey(fp, key, cb);
+#else
+    rc = PEM_read_PrivateKey(fp, key, cb, NULL);
+#endif
+    if (rc == NULL) {
+        /* 2. try DER+Base64 */
+        fseek(fp, 0L, SEEK_SET);
+        if ((bioS = BIO_new(BIO_s_fd())) == NULL)
+            return NULL;
+        BIO_set_fd(bioS, fileno(fp), BIO_NOCLOSE);
+        if ((bioF = BIO_new(BIO_f_base64())) == NULL) {
+            BIO_free(bioS);
+            return NULL;
+        }
+        bioS = BIO_push(bioF, bioS);
+        rc = d2i_PrivateKey_bio(bioS, NULL);
+        BIO_free_all(bioS);
+        if (rc == NULL) {
+            /* 3. try plain DER */
+            fseek(fp, 0L, SEEK_SET);
+            if ((bioS = BIO_new(BIO_s_fd())) == NULL)
+                return NULL;
+            BIO_set_fd(bioS, fileno(fp), BIO_NOCLOSE);
+            rc = d2i_PrivateKey_bio(bioS, NULL);
+            BIO_free(bioS);
+        }
+    }
+    if (rc != NULL && key != NULL) {
+        if (*key != NULL)
+            EVP_PKEY_free(*key);
+        *key = rc;
+    }
+    return rc;
+}
+
+/*  _________________________________________________________________
+**
+**  Smart shutdown
+**  _________________________________________________________________
+*/
+
+int SSL_smart_shutdown(SSL *ssl)
+{
+    int i;
+    int rc;
+
+    /*
+     * Repeat the calls, because SSL_shutdown internally dispatches through a
+     * little state machine. Usually only one or two interation should be
+     * needed, so we restrict the total number of restrictions in order to
+     * avoid process hangs in case the client played bad with the socket
+     * connection and OpenSSL cannot recognize it.
+     */
+    rc = 0;
+    for (i = 0; i < 4 /* max 2x pending + 2x data = 4 */; i++) {
+        if ((rc = SSL_shutdown(ssl)))
+            break;
+    }
+    return rc;
+}
+
+/*  _________________________________________________________________
+**
+**  Certificate Revocation List (CRL) Storage
+**  _________________________________________________________________
+*/
+
+X509_STORE *SSL_X509_STORE_create(char *cpFile, char *cpPath)
+{
+    X509_STORE *pStore;
+    X509_LOOKUP *pLookup;
+
+    if (cpFile == NULL && cpPath == NULL)
+        return NULL;
+    if ((pStore = X509_STORE_new()) == NULL)
+        return NULL;
+    if (cpFile != NULL) {
+        if ((pLookup = X509_STORE_add_lookup(pStore, X509_LOOKUP_file())) == NULL) {
+            X509_STORE_free(pStore);
+            return NULL;
+        }
+        X509_LOOKUP_load_file(pLookup, cpFile, X509_FILETYPE_PEM);
+    }
+    if (cpPath != NULL) {
+        if ((pLookup = X509_STORE_add_lookup(pStore, X509_LOOKUP_hash_dir())) == NULL) {
+            X509_STORE_free(pStore);
+            return NULL;
+        }
+        X509_LOOKUP_add_dir(pLookup, cpPath, X509_FILETYPE_PEM);
+    }
+    return pStore;
+}
+
+int SSL_X509_STORE_lookup(X509_STORE *pStore, int nType,
+                          X509_NAME *pName, X509_OBJECT *pObj)
+{
+    X509_STORE_CTX pStoreCtx;
+    int rc;
+
+    X509_STORE_CTX_init(&pStoreCtx, pStore, NULL, NULL);
+    rc = X509_STORE_get_by_subject(&pStoreCtx, nType, pName, pObj);
+    X509_STORE_CTX_cleanup(&pStoreCtx);
+    return rc;
+}
+
+/*  _________________________________________________________________
+**
+**  Cipher Suite Spec String Creation
+**  _________________________________________________________________
+*/
+
+char *SSL_make_ciphersuite(pool *p, SSL *ssl)
+{
+    STACK_OF(SSL_CIPHER) *sk;
+    SSL_CIPHER *c;
+    int i;
+    int l;
+    char *cpCipherSuite;
+    char *cp;
+
+    if (ssl == NULL) 
+        return "";
+    if ((sk = SSL_get_ciphers(ssl)) == NULL)
+        return "";
+    l = 0;
+    for (i = 0; i < sk_SSL_CIPHER_num(sk); i++) {
+        c = sk_SSL_CIPHER_value(sk, i);
+        l += strlen(c->name)+2+1;
+    }
+    if (l == 0)
+        return "";
+    cpCipherSuite = (char *)ap_palloc(p, l+1);
+    cp = cpCipherSuite;
+    for (i = 0; i < sk_SSL_CIPHER_num(sk); i++) {
+        c = sk_SSL_CIPHER_value(sk, i);
+        l = strlen(c->name);
+        memcpy(cp, c->name, l);
+        cp += l;
+        *cp++ = '/';
+        *cp++ = (c->valid == 1 ? '1' : '0');
+        *cp++ = ':';
+    }
+    *(cp-1) = NUL;
+    return cpCipherSuite;
+}
+
+/*  _________________________________________________________________
+**
+**  Certificate Checks
+**  _________________________________________________________________
+*/
+
+/* check whether cert contains extended key usage with a SGC tag */
+BOOL SSL_X509_isSGC(X509 *cert)
+{
+    X509_EXTENSION *ext;
+    int ext_nid;
+    STACK *sk;
+    BOOL is_sgc;
+    int idx;
+    int i;
+    
+    is_sgc = FALSE;
+    idx = X509_get_ext_by_NID(cert, NID_ext_key_usage, -1);
+    if (idx >= 0) {
+        ext = X509_get_ext(cert, idx);
+        if ((sk = (STACK *)X509V3_EXT_d2i(ext)) != NULL) {
+            for (i = 0; i < sk_num(sk); i++) {
+                ext_nid = OBJ_obj2nid((ASN1_OBJECT *)sk_value(sk, i));
+                if (ext_nid == NID_ms_sgc || ext_nid == NID_ns_sgc) {
+                    is_sgc = TRUE;
+                    break;
+                }
+            }
+        }
+    }
+    return is_sgc;
+}
+
+/* retrieve basic constraints ingredients */
+BOOL SSL_X509_getBC(X509 *cert, int *ca, int *pathlen)
+{
+    X509_EXTENSION *ext;
+    BASIC_CONSTRAINTS *bc;
+    int idx;
+    BIGNUM *bn = NULL;
+    char *cp;
+    
+    if ((idx = X509_get_ext_by_NID(cert, NID_basic_constraints, -1)) < 0)
+        return FALSE;
+    ext = X509_get_ext(cert, idx);
+    if (ext == NULL)
+        return FALSE;
+    if ((bc = (BASIC_CONSTRAINTS *)X509V3_EXT_d2i(ext)) == NULL)
+        return FALSE;
+    *ca = bc->ca;
+    *pathlen = -1 /* unlimited */;
+    if (bc->pathlen != NULL) {
+        if ((bn = ASN1_INTEGER_to_BN(bc->pathlen, NULL)) == NULL)
+            return FALSE;
+        if ((cp = BN_bn2dec(bn)) == NULL)
+            return FALSE;
+        *pathlen = atoi(cp);
+        free(cp);
+        BN_free(bn);
+    }
+    BASIC_CONSTRAINTS_free(bc);
+    return TRUE;
+}
+
+/* retrieve subject CommonName of certificate */
+BOOL SSL_X509_getCN(pool *p, X509 *xs, char **cppCN)
+{
+    X509_NAME *xsn;
+    X509_NAME_ENTRY *xsne;
+    int i, nid;
+
+    xsn = X509_get_subject_name(xs);
+    for (i = 0; i < sk_X509_NAME_ENTRY_num(xsn->entries); i++) {
+        xsne = sk_X509_NAME_ENTRY_value(xsn->entries, i);
+        nid = OBJ_obj2nid(xsne->object);
+        if (nid == NID_commonName) {
+            *cppCN = ap_palloc(p, xsne->value->length+1);
+            ap_cpystrn(*cppCN, (char *)xsne->value->data, xsne->value->length+1);
+            (*cppCN)[xsne->value->length] = NUL;
+#ifdef CHARSET_EBCDIC
+            ascii2ebcdic(*cppCN, *cppCN, strlen(*cppCN));
+#endif
+            return TRUE;
+        }
+    }
+    return FALSE;
+}
+
+/*  _________________________________________________________________
+**
+**  Low-Level CA Certificate Loading
+**  _________________________________________________________________
+*/
+
+#ifdef SSL_EXPERIMENTAL_PROXY
+
+BOOL SSL_load_CrtAndKeyInfo_file(pool *p, STACK_OF(X509_INFO) *sk, char *filename)
+{
+    BIO *in;
+
+    if ((in = BIO_new(BIO_s_file())) == NULL)
+        return FALSE;
+    if (BIO_read_filename(in, filename) <= 0) {
+        BIO_free(in);
+        return FALSE;
+    }
+    ERR_clear_error();
+#if SSL_LIBRARY_VERSION < 0x00904000
+    PEM_X509_INFO_read_bio(in, sk, NULL);
+#else
+    PEM_X509_INFO_read_bio(in, sk, NULL, NULL);
+#endif
+    BIO_free(in);
+    return TRUE;
+}
+
+BOOL SSL_load_CrtAndKeyInfo_path(pool *p, STACK_OF(X509_INFO) *sk, char *pathname)
+{
+    struct stat st;
+    DIR *dir;
+    pool *sp;
+    struct dirent *nextent;
+    char *fullname;
+    BOOL ok;
+
+    sp = ap_make_sub_pool(p);
+    if ((dir = ap_popendir(sp, pathname)) == NULL) {
+        ap_destroy_pool(sp);
+        return FALSE;
+    }
+    ok = FALSE;
+    while ((nextent = readdir(dir)) != NULL) {
+        fullname = ap_pstrcat(sp, pathname, "/", nextent->d_name, NULL);
+        if (stat(fullname, &st) != 0)
+            continue;
+        if (!S_ISREG(st.st_mode))
+            continue;
+        if (SSL_load_CrtAndKeyInfo_file(sp, sk, fullname))
+            ok = TRUE;
+    }
+    ap_pclosedir(p, dir);
+    ap_destroy_pool(sp);
+    return ok;
+}              
+
+#endif /* SSL_EXPERIMENTAL_PROXY */
+
+/*  _________________________________________________________________
+**
+**  Extra Server Certificate Chain Support
+**  _________________________________________________________________
+*/
+
+/* 
+ * Read a file that optionally contains the server certificate in PEM
+ * format, possibly followed by a sequence of CA certificates that
+ * should be sent to the peer in the SSL Certificate message.
+ */
+int SSL_CTX_use_certificate_chain(
+    SSL_CTX *ctx, char *file, int skipfirst, int (*cb)())
+{
+    BIO *bio;
+    X509 *x509;
+    unsigned long err;
+    int n;
+
+    if ((bio = BIO_new(BIO_s_file_internal())) == NULL)
+        return -1;
+    if (BIO_read_filename(bio, file) <= 0) {
+        BIO_free(bio);
+        return -1;
+    }
+    /* optionally skip a leading server certificate */
+    if (skipfirst) {
+#if SSL_LIBRARY_VERSION < 0x00904000
+        if ((x509 = PEM_read_bio_X509(bio, NULL, cb)) == NULL) {
+#else
+        if ((x509 = PEM_read_bio_X509(bio, NULL, cb, NULL)) == NULL) {
+#endif
+            BIO_free(bio);
+            return -1;
+        }
+        X509_free(x509);
+    }
+    /* free a perhaps already configured extra chain */
+    if (ctx->extra_certs != NULL) {
+        sk_X509_pop_free(ctx->extra_certs, X509_free);
+        ctx->extra_certs = NULL;
+    }
+    /* create new extra chain by loading the certs */
+    n = 0;
+#if SSL_LIBRARY_VERSION < 0x00904000
+    while ((x509 = PEM_read_bio_X509(bio, NULL, cb)) != NULL) {
+#else
+    while ((x509 = PEM_read_bio_X509(bio, NULL, cb, NULL)) != NULL) {
+#endif
+        if (!SSL_CTX_add_extra_chain_cert(ctx, x509)) { 
+            X509_free(x509);
+            BIO_free(bio);
+            return -1;
+        }
+        n++;
+    }
+    /* Make sure that only the error is just an EOF */
+    if ((err = ERR_peek_error()) > 0) {
+        if (!(   ERR_GET_LIB(err) == ERR_LIB_PEM 
+              && ERR_GET_REASON(err) == PEM_R_NO_START_LINE)) {
+            BIO_free(bio);
+            return -1;
+        }
+        while (ERR_get_error() > 0) ;
+    }
+    BIO_free(bio);
+    return n;
+}
+
+/*  _________________________________________________________________
+**
+**  Session Stuff
+**  _________________________________________________________________
+*/
+
+char *SSL_SESSION_id2sz(unsigned char *id, int idlen)
+{
+    static char str[(SSL_MAX_SSL_SESSION_ID_LENGTH+1)*2];
+    char *cp;
+    int n;
+
+    cp = str;
+    for (n = 0; n < idlen && n < SSL_MAX_SSL_SESSION_ID_LENGTH; n++) {
+        ap_snprintf(cp, sizeof(str)-(cp-str), "%02X", id[n]);
+        cp += 2;
+    }
+    *cp = NUL;
+    return str;
+}
+
diff --git a/modules/ssl/ssl_util_ssl.h b/modules/ssl/ssl_util_ssl.h
new file mode 100644 (file)
index 0000000..23aaaaa
--- /dev/null
@@ -0,0 +1,115 @@
+/*                      _             _
+**  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
+** | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
+** | | | | | | (_) | (_| |   \__ \__ \ |  www.modssl.org
+** |_| |_| |_|\___/ \__,_|___|___/___/_|  ftp.modssl.org
+**                      |_____|
+**  ssl_util_ssl.h
+**  Additional Utility Functions for OpenSSL
+*/
+
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ *    nor may "mod_ssl" appear in their names without prior
+ *    written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#ifndef SSL_UTIL_SSL_H
+#define SSL_UTIL_SSL_H
+
+/*
+ * Determine SSL library version number
+ */
+#ifdef OPENSSL_VERSION_NUMBER
+#define SSL_LIBRARY_VERSION OPENSSL_VERSION_NUMBER
+#define SSL_LIBRARY_NAME    "OpenSSL"
+#define SSL_LIBRARY_TEXT    OPENSSL_VERSION_TEXT
+#else
+#define SSL_LIBRARY_VERSION 0x0000
+#define SSL_LIBRARY_NAME    "OtherSSL"
+#define SSL_LIBRARY_TEXT    "OtherSSL 0.0.0 00 XXX 0000"
+#endif
+
+/*
+ * Support for retrieving/overriding states
+ */
+#ifndef SSL_get_state
+#define SSL_get_state(ssl) SSL_state(ssl)
+#endif
+#define SSL_set_state(ssl,val) (ssl)->state = val
+
+/*
+ *  Maximum length of a DER encoded session.
+ *  FIXME: There is no define in OpenSSL, but OpenSSL uses 1024*10,
+ *         so this value should be ok. Although we have no warm feeling.
+ */
+#define SSL_SESSION_MAX_DER 1024*10
+
+/*  
+ *  Additional Functions
+ */
+int         SSL_get_app_data2_idx(void);
+void       *SSL_get_app_data2(SSL *);
+void        SSL_set_app_data2(SSL *, void *);
+X509       *SSL_read_X509(FILE *, X509 **, int (*)());
+EVP_PKEY   *SSL_read_PrivateKey(FILE *, EVP_PKEY **, int (*)());
+int         SSL_smart_shutdown(SSL *ssl);
+X509_STORE *SSL_X509_STORE_create(char *, char *);
+int         SSL_X509_STORE_lookup(X509_STORE *, int, X509_NAME *, X509_OBJECT *);
+char       *SSL_make_ciphersuite(pool *, SSL *);
+BOOL        SSL_X509_isSGC(X509 *);
+BOOL        SSL_X509_getBC(X509 *, int *, int *);
+BOOL        SSL_X509_getCN(pool *, X509 *, char **);
+#ifdef SSL_EXPERIMENTAL_PROXY
+BOOL        SSL_load_CrtAndKeyInfo_file(pool *, STACK_OF(X509_INFO) *, char *);
+BOOL        SSL_load_CrtAndKeyInfo_path(pool *, STACK_OF(X509_INFO) *, char *);
+#endif /* SSL_EXPERIMENTAL_PROXY */
+int         SSL_CTX_use_certificate_chain(SSL_CTX *, char *, int, int (*)());
+char       *SSL_SESSION_id2sz(unsigned char *, int);
+
+#endif /* SSL_UTIL_SSL_H */
diff --git a/modules/ssl/ssl_util_table.c b/modules/ssl/ssl_util_table.c
new file mode 100644 (file)
index 0000000..f68c6dd
--- /dev/null
@@ -0,0 +1,2868 @@
+/*                      _             _
+**  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
+** | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
+** | | | | | | (_) | (_| |   \__ \__ \ |  www.modssl.org
+** |_| |_| |_|\___/ \__,_|___|___/___/_|  ftp.modssl.org
+**                      |_____|
+**  ssl_util_table.c
+**  High Performance Hash Table Functions
+*/
+
+/* ====================================================================
+ * Copyright (c) 1999-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ *    nor may "mod_ssl" appear in their names without prior
+ *    written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+/*
+ * Generic hash table handler
+ * Table 4.1.0 July-28-1998
+ *
+ * This library is a generic open hash table with buckets and
+ * linked lists.  It is pretty high performance.  Each element
+ * has a key and a data.  The user indexes on the key to find the
+ * data.
+ *
+ * Copyright 1998 by Gray Watson <gray@letters.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose and without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies,
+ * and that the name of Gray Watson not be used in advertising or
+ * publicity pertaining to distribution of the document or software
+ * without specific, written prior permission.
+ *
+ * Gray Watson makes no representations about the suitability of the
+ * software described herein for any purpose.  It is provided "as is"
+ * without express or implied warranty.
+ *
+ * Modified in March 1999 by Ralf S. Engelschall <rse@engelschall.com>
+ * for use in the mod_ssl project:
+ *   o merged table_loc.h header into table.c
+ *   o removed fillproto-comments from table.h
+ *   o removed mmap() support because it's too unportable
+ *   o added support for MM library via ta_{malloc,calloc,realloc,free}
+ */
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef WIN32
+#include <io.h>
+#include <errno.h>
+#else
+#include <unistd.h>
+#endif
+
+/* forward definitions for table.h */
+typedef struct table_st table_t;
+typedef struct table_entry_st table_entry_t;
+
+#define TABLE_PRIVATE
+#include "ssl_util_table.h"
+
+/****************************** local defines ******************************/
+
+#ifndef BITSPERBYTE
+#define BITSPERBYTE     8
+#endif
+#ifndef BITS
+#define BITS(type)      (BITSPERBYTE * (int)sizeof(type))
+#endif
+
+#define TABLE_MAGIC     0xBADF00D       /* very magic magicness */
+#define LINEAR_MAGIC    0xAD00D00       /* magic value for linear struct */
+#define DEFAULT_SIZE    1024    /* default table size */
+#define MAX_ALIGNMENT   128     /* max alignment value */
+#define MAX_SORT_SPLITS 128     /* qsort can handle 2^128 entries */
+
+/* returns 1 when we should grow or shrink the table */
+#define SHOULD_TABLE_GROW(tab)  ((tab)->ta_entry_n > (tab)->ta_bucket_n * 2)
+#define SHOULD_TABLE_SHRINK(tab) ((tab)->ta_entry_n < (tab)->ta_bucket_n / 2)
+
+/*
+ * void HASH_MIX
+ *
+ * DESCRIPTION:
+ *
+ * Mix 3 32-bit values reversibly.  For every delta with one or two bits
+ * set, and the deltas of all three high bits or all three low bits,
+ * whether the original value of a,b,c is almost all zero or is
+ * uniformly distributed.
+ *
+ * If HASH_MIX() is run forward or backward, at least 32 bits in a,b,c
+ * have at least 1/4 probability of changing.  If mix() is run
+ * forward, every bit of c will change between 1/3 and 2/3 of the
+ * time.  (Well, 22/100 and 78/100 for some 2-bit deltas.)
+ *
+ * HASH_MIX() takes 36 machine instructions, but only 18 cycles on a
+ * superscalar machine (like a Pentium or a Sparc).  No faster mixer
+ * seems to work, that's the result of my brute-force search.  There
+ * were about 2^68 hashes to choose from.  I only tested about a
+ * billion of those.
+ */
+#define HASH_MIX(a, b, c) \
+ do { \
+   a -= b; a -= c; a ^= (c >> 13); \
+   b -= c; b -= a; b ^= (a << 8); \
+   c -= a; c -= b; c ^= (b >> 13); \
+   a -= b; a -= c; a ^= (c >> 12); \
+   b -= c; b -= a; b ^= (a << 16); \
+   c -= a; c -= b; c ^= (b >> 5); \
+   a -= b; a -= c; a ^= (c >> 3); \
+   b -= c; b -= a; b ^= (a << 10); \
+   c -= a; c -= b; c ^= (b >> 15); \
+ } while(0)
+
+#define TABLE_POINTER(table, type, pnt)         (pnt)
+
+/*
+ * Macros to get at the key and the data pointers
+ */
+#define ENTRY_KEY_BUF(entry_p)          ((entry_p)->te_key_buf)
+#define ENTRY_DATA_BUF(tab_p, entry_p)  \
+     (ENTRY_KEY_BUF(entry_p) + (entry_p)->te_key_size)
+
+/*
+ * Table structures...
+ */
+
+/*
+ * HACK: this should be equiv as the table_entry_t without the key_buf
+ * char.  We use this with the ENTRY_SIZE() macro above which solves
+ * the problem with the lack of the [0] GNU hack.  We use the
+ * table_entry_t structure to better map the memory and make things
+ * faster.
+ */
+typedef struct table_shell_st {
+    unsigned int te_key_size;   /* size of data */
+    unsigned int te_data_size;  /* size of data */
+    struct table_shell_st *te_next_p;   /* pointer to next in the list */
+    /* NOTE: this does not have the te_key_buf field here */
+} table_shell_t;
+
+/*
+ * Elements in the bucket linked-lists.  The key[1] is the start of
+ * the key with the rest of the key and all of the data information
+ * packed in memory directly after the end of this structure.
+ *
+ * NOTE: if this structure is changed, the table_shell_t must be changed
+ * to match.
+ */
+struct table_entry_st {
+    unsigned int te_key_size;   /* size of data */
+    unsigned int te_data_size;  /* size of data */
+    struct table_entry_st *te_next_p;   /* pointer to next in the list */
+    unsigned char te_key_buf[1];        /* 1st byte of key buf */
+};
+
+/* external structure for debuggers be able to see void */
+typedef table_entry_t table_entry_ext_t;
+
+/* main table structure */
+struct table_st {
+    unsigned int ta_magic;      /* magic number */
+    unsigned int ta_flags;      /* table's flags defined in table.h */
+    unsigned int ta_bucket_n;   /* num of buckets, should be 2^X */
+    unsigned int ta_entry_n;    /* num of entries in all buckets */
+    unsigned int ta_data_align; /* data alignment value */
+    table_entry_t **ta_buckets; /* array of linked lists */
+    table_linear_t ta_linear;   /* linear tracking */
+    unsigned long ta_file_size; /* size of on-disk space */
+    void *(*ta_malloc)(size_t size);
+    void *(*ta_calloc)(size_t number, size_t size);
+    void *(*ta_realloc)(void *ptr, size_t size);
+    void (*ta_free)(void *ptr);
+};
+
+/* external table structure for debuggers */
+typedef table_t table_ext_t;
+
+/* local comparison functions */
+typedef int (*compare_t) (const void *element1_p, const void *element2_p,
+                          table_compare_t user_compare,
+                          const table_t * table_p);
+
+/*
+ * to map error to string
+ */
+typedef struct {
+    int es_error;               /* error number */
+    char *es_string;            /* assocaited string */
+} error_str_t;
+
+static error_str_t errors[] =
+{
+    {TABLE_ERROR_NONE, "no error"},
+    {TABLE_ERROR_PNT, "invalid table pointer"},
+    {TABLE_ERROR_ARG_NULL, "buffer argument is null"},
+    {TABLE_ERROR_SIZE, "incorrect size argument"},
+    {TABLE_ERROR_OVERWRITE, "key exists and no overwrite"},
+    {TABLE_ERROR_NOT_FOUND, "key does not exist"},
+    {TABLE_ERROR_ALLOC, "error allocating memory"},
+    {TABLE_ERROR_LINEAR, "linear access not in progress"},
+    {TABLE_ERROR_OPEN, "could not open file"},
+    {TABLE_ERROR_SEEK, "could not seek to position in file"},
+    {TABLE_ERROR_READ, "could not read from file"},
+    {TABLE_ERROR_WRITE, "could not write to file"},
+    {TABLE_ERROR_EMPTY, "table is empty"},
+    {TABLE_ERROR_NOT_EMPTY, "table contains data"},
+    {TABLE_ERROR_ALIGNMENT, "invalid alignment value"},
+    {0}
+};
+
+#define INVALID_ERROR   "invalid error code"
+
+/****************************** local functions ******************************/
+
+/*
+ * static table_entry_t *first_entry
+ *
+ * DESCRIPTION:
+ *
+ * Return the first entry in the table.  It will set the linear
+ * structure counter to the position of the first entry.
+ *
+ * RETURNS:
+ *
+ * Success: A pointer to the first entry in the table.
+ *
+ * Failure: NULL if there is no first entry.
+ *
+ * ARGUMENTS:
+ *
+ * table_p - Table whose next entry we are finding.
+ *
+ * linear_p - Pointer to a linear structure which we will advance and
+ * then find the corresponding entry.
+ */
+static table_entry_t *first_entry(table_t * table_p,
+                                  table_linear_t * linear_p)
+{
+    table_entry_t *entry_p;
+    unsigned int bucket_c = 0;
+
+    /* look for the first non-empty bucket */
+    for (bucket_c = 0; bucket_c < table_p->ta_bucket_n; bucket_c++) {
+        entry_p = table_p->ta_buckets[bucket_c];
+        if (entry_p != NULL) {
+            if (linear_p != NULL) {
+                linear_p->tl_bucket_c = bucket_c;
+                linear_p->tl_entry_c = 0;
+            }
+            return TABLE_POINTER(table_p, table_entry_t *, entry_p);
+        }
+    }
+
+    return NULL;
+}
+
+/*
+ * static table_entry_t *next_entry
+ *
+ * DESCRIPTION:
+ *
+ * Return the next entry in the table which is past the position in
+ * our linear pointer.  It will advance the linear structure counters.
+ *
+ * RETURNS:
+ *
+ * Success: A pointer to the next entry in the table.
+ *
+ * Failure: NULL.
+ *
+ * ARGUMENTS:
+ *
+ * table_p - Table whose next entry we are finding.
+ *
+ * linear_p - Pointer to a linear structure which we will advance and
+ * then find the corresponding entry.
+ *
+ * error_p - Pointer to an integer which when the routine returns will
+ * contain a table error code.
+ */
+static table_entry_t *next_entry(table_t * table_p, table_linear_t * linear_p,
+                                 int *error_p)
+{
+    table_entry_t *entry_p;
+    int entry_c;
+
+    /* can't next if we haven't first-ed */
+    if (linear_p == NULL) {
+        if (error_p != NULL)
+            *error_p = TABLE_ERROR_LINEAR;
+        return NULL;
+    }
+
+    if (linear_p->tl_bucket_c >= table_p->ta_bucket_n) {
+        /*
+         * NOTE: this might happen if we delete an item which shortens the
+         * table bucket numbers.
+         */
+        if (error_p != NULL)
+            *error_p = TABLE_ERROR_NOT_FOUND;
+        return NULL;
+    }
+
+    linear_p->tl_entry_c++;
+
+    /* find the entry which is the nth in the list */
+    entry_p = table_p->ta_buckets[linear_p->tl_bucket_c];
+    /* NOTE: we swap the order here to be more efficient */
+    for (entry_c = linear_p->tl_entry_c; entry_c > 0; entry_c--) {
+        /* did we reach the end of the list? */
+        if (entry_p == NULL)
+            break;
+        entry_p = TABLE_POINTER(table_p, table_entry_t *, entry_p)->te_next_p;
+    }
+
+    /* did we find an entry in the current bucket? */
+    if (entry_p != NULL) {
+        if (error_p != NULL)
+            *error_p = TABLE_ERROR_NONE;
+        return TABLE_POINTER(table_p, table_entry_t *, entry_p);
+    }
+
+    /* find the first entry in the next non-empty bucket */
+
+    linear_p->tl_entry_c = 0;
+    for (linear_p->tl_bucket_c++; linear_p->tl_bucket_c < table_p->ta_bucket_n;
+         linear_p->tl_bucket_c++) {
+        entry_p = table_p->ta_buckets[linear_p->tl_bucket_c];
+        if (entry_p != NULL) {
+            if (error_p != NULL)
+                *error_p = TABLE_ERROR_NONE;
+            return TABLE_POINTER(table_p, table_entry_t *, entry_p);
+        }
+    }
+
+    if (error_p != NULL)
+        *error_p = TABLE_ERROR_NOT_FOUND;
+    return NULL;
+}
+
+/*
+ * static unsigned int hash
+ *
+ * DESCRIPTION:
+ *
+ * Hash a variable-length key into a 32-bit value.  Every bit of the
+ * key affects every bit of the return value.  Every 1-bit and 2-bit
+ * delta achieves avalanche.  About (6 * len + 35) instructions.  The
+ * best hash table sizes are powers of 2.  There is no need to use mod
+ * (sooo slow!).  If you need less than 32 bits, use a bitmask.  For
+ * example, if you need only 10 bits, do h = (h & hashmask(10)); In
+ * which case, the hash table should have hashsize(10) elements.
+ *
+ * By Bob Jenkins, 1996.  bob_jenkins@compuserve.com.  You may use
+ * this code any way you wish, private, educational, or commercial.
+ * It's free.  See
+ * http://ourworld.compuserve.com/homepages/bob_jenkins/evahash.htm
+ * Use for hash table lookup, or anything where one collision in 2^^32
+ * is acceptable.  Do NOT use for cryptographic purposes.
+ *
+ * RETURNS:
+ *
+ * Returns a 32-bit hash value.
+ *
+ * ARGUMENTS:
+ *
+ * key - Key (the unaligned variable-length array of bytes) that we
+ * are hashing.
+ *
+ * length - Length of the key in bytes.
+ *
+ * init_val - Initialization value of the hash if you need to hash a
+ * number of strings together.  For instance, if you are hashing N
+ * strings (unsigned char **)keys, do it like this:
+ *
+ * for (i=0, h=0; i<N; ++i) h = hash( keys[i], len[i], h);
+ */
+static unsigned int hash(const unsigned char *key,
+                         const unsigned int length,
+                         const unsigned int init_val)
+{
+    const unsigned char *key_p = key;
+    unsigned int a, b, c, len;
+
+    /* set up the internal state */
+    a = 0x9e3779b9;             /* the golden ratio; an arbitrary value */
+    b = 0x9e3779b9;
+    c = init_val;               /* the previous hash value */
+
+    /* handle most of the key */
+    for (len = length; len >= 12; len -= 12) {
+        a += (key_p[0]
+              + ((unsigned long) key_p[1] << 8)
+              + ((unsigned long) key_p[2] << 16)
+              + ((unsigned long) key_p[3] << 24));
+        b += (key_p[4]
+              + ((unsigned long) key_p[5] << 8)
+              + ((unsigned long) key_p[6] << 16)
+              + ((unsigned long) key_p[7] << 24));
+        c += (key_p[8]
+              + ((unsigned long) key_p[9] << 8)
+              + ((unsigned long) key_p[10] << 16)
+              + ((unsigned long) key_p[11] << 24));
+        HASH_MIX(a, b, c);
+        key_p += 12;
+    }
+
+    c += length;
+
+    /* all the case statements fall through to the next */
+    switch (len) {
+    case 11:
+        c += ((unsigned long) key_p[10] << 24);
+    case 10:
+        c += ((unsigned long) key_p[9] << 16);
+    case 9:
+        c += ((unsigned long) key_p[8] << 8);
+        /* the first byte of c is reserved for the length */
+    case 8:
+        b += ((unsigned long) key_p[7] << 24);
+    case 7:
+        b += ((unsigned long) key_p[6] << 16);
+    case 6:
+        b += ((unsigned long) key_p[5] << 8);
+    case 5:
+        b += key_p[4];
+    case 4:
+        a += ((unsigned long) key_p[3] << 24);
+    case 3:
+        a += ((unsigned long) key_p[2] << 16);
+    case 2:
+        a += ((unsigned long) key_p[1] << 8);
+    case 1:
+        a += key_p[0];
+        /* case 0: nothing left to add */
+    }
+    HASH_MIX(a, b, c);
+
+    return c;
+}
+
+/*
+ * static int entry_size
+ *
+ * DESCRIPTION:
+ *
+ * Calculates the appropriate size of an entry to include the key and
+ * data sizes as well as any associated alignment to the data.
+ *
+ * RETURNS:
+ *
+ * The associated size of the entry.
+ *
+ * ARGUMENTS:
+ *
+ * table_p - Table associated with the entries whose size we are
+ * determining.
+ *
+ * key_size - Size of the entry key.
+ *
+ * data - Size of the entry data.
+ */
+static int entry_size(const table_t * table_p, const unsigned int key_size,
+                      const unsigned int data_size)
+{
+    int size, left;
+
+    /* initial size -- key is already aligned if right after struct */
+    size = sizeof(struct table_shell_st) + key_size;
+
+    /* if there is no alignment then it is easy */
+    if (table_p->ta_data_align == 0)
+        return size + data_size;
+    /* add in our alignement */
+    left = size & (table_p->ta_data_align - 1);
+    if (left > 0)
+        size += table_p->ta_data_align - left;
+    /* we add the data size here after the alignment */
+    size += data_size;
+
+    return size;
+}
+
+/*
+ * static unsigned char *entry_data_buf
+ *
+ * DESCRIPTION:
+ *
+ * Companion to the ENTRY_DATA_BUF macro but this handles any
+ * associated alignment to the data in the entry.
+ *
+ * RETURNS:
+ *
+ * Pointer to the data segment of the entry.
+ *
+ * ARGUMENTS:
+ *
+ * table_p - Table associated with the entry.
+ *
+ * entry_p - Entry whose data pointer we are determining.
+ */
+static unsigned char *entry_data_buf(const table_t * table_p,
+                                     const table_entry_t * entry_p)
+{
+    const unsigned char *buf_p;
+    int size, pad;
+
+    buf_p = entry_p->te_key_buf + entry_p->te_key_size;
+
+    /* if there is no alignment then it is easy */
+    if (table_p->ta_data_align == 0)
+        return (unsigned char *) buf_p;
+    /* we need the size of the space before the data */
+    size = sizeof(struct table_shell_st) + entry_p->te_key_size;
+
+    /* add in our alignment */
+    pad = size & (table_p->ta_data_align - 1);
+    if (pad > 0)
+        pad = table_p->ta_data_align - pad;
+    return (unsigned char *) buf_p + pad;
+}
+
+/******************************* sort routines *******************************/
+
+/*
+ * static int our_compare
+ *
+ * DESCRIPTION:
+ *
+ * Compare two entries by calling user's compare program or by using
+ * memcmp.
+ *
+ * RETURNS:
+ *
+ * < 0, == 0, or > 0 depending on whether p1 is > p2, == p2, < p2.
+ *
+ * ARGUMENTS:
+ *
+ * p1 - First entry pointer to compare.
+ *
+ * p2 - Second entry pointer to compare.
+ *
+ * compare - User comparison function.  Ignored.
+ *
+ * table_p - Associated table being ordered.  Ignored.
+ */
+static int local_compare(const void *p1, const void *p2,
+                         table_compare_t compare, const table_t * table_p)
+{
+    const table_entry_t *const *ent1_p = p1, *const *ent2_p = p2;
+    int cmp;
+    unsigned int size;
+
+    /* compare as many bytes as we can */
+    size = (*ent1_p)->te_key_size;
+    if ((*ent2_p)->te_key_size < size)
+        size = (*ent2_p)->te_key_size;
+    cmp = memcmp(ENTRY_KEY_BUF(*ent1_p), ENTRY_KEY_BUF(*ent2_p), size);
+    /* if common-size equal, then if next more bytes, it is larger */
+    if (cmp == 0)
+        cmp = (*ent1_p)->te_key_size - (*ent2_p)->te_key_size;
+    return cmp;
+}
+
+/*
+ * static int external_compare
+ *
+ * DESCRIPTION:
+ *
+ * Compare two entries by calling user's compare program or by using
+ * memcmp.
+ *
+ * RETURNS:
+ *
+ * < 0, == 0, or > 0 depending on whether p1 is > p2, == p2, < p2.
+ *
+ * ARGUMENTS:
+ *
+ * p1 - First entry pointer to compare.
+ *
+ * p2 - Second entry pointer to compare.
+ *
+ * user_compare - User comparison function.
+ *
+ * table_p - Associated table being ordered.
+ */
+static int external_compare(const void *p1, const void *p2,
+                            table_compare_t user_compare,
+                            const table_t * table_p)
+{
+    const table_entry_t *const *ent1_p = p1, *const *ent2_p = p2;
+    /* since we know we are not aligned we can use the EXTRY_DATA_BUF macro */
+    return user_compare(ENTRY_KEY_BUF(*ent1_p), (*ent1_p)->te_key_size,
+                        ENTRY_DATA_BUF(table_p, *ent1_p),
+                        (*ent1_p)->te_data_size,
+                        ENTRY_KEY_BUF(*ent2_p), (*ent2_p)->te_key_size,
+                        ENTRY_DATA_BUF(table_p, *ent2_p),
+                        (*ent2_p)->te_data_size);
+}
+
+/*
+ * static int external_compare_align
+ *
+ * DESCRIPTION:
+ *
+ * Compare two entries by calling user's compare program or by using
+ * memcmp.  Alignment information is necessary.
+ *
+ * RETURNS:
+ *
+ * < 0, == 0, or > 0 depending on whether p1 is > p2, == p2, < p2.
+ *
+ * ARGUMENTS:
+ *
+ * p1 - First entry pointer to compare.
+ *
+ * p2 - Second entry pointer to compare.
+ *
+ * user_compare - User comparison function.
+ *
+ * table_p - Associated table being ordered.
+ */
+static int external_compare_align(const void *p1, const void *p2,
+                                  table_compare_t user_compare,
+                                  const table_t * table_p)
+{
+    const table_entry_t *const *ent1_p = p1, *const *ent2_p = p2;
+    /* since we are aligned we have to use the entry_data_buf function */
+    return user_compare(ENTRY_KEY_BUF(*ent1_p), (*ent1_p)->te_key_size,
+                        entry_data_buf(table_p, *ent1_p),
+                        (*ent1_p)->te_data_size,
+                        ENTRY_KEY_BUF(*ent2_p), (*ent2_p)->te_key_size,
+                        entry_data_buf(table_p, *ent2_p),
+                        (*ent2_p)->te_data_size);
+}
+
+/*
+ * static void split
+ *
+ * DESCRIPTION:
+ *
+ * This sorts an array of longs via the quick sort algorithm (it's
+ * pretty quick)
+ *
+ * RETURNS:
+ *
+ * None.
+ *
+ * ARGUMENTS:
+ *
+ * first_p - Start of the list that we are splitting.
+ *
+ * last_p - Last entry in the list that we are splitting.
+ *
+ * compare - Comparison function which is handling the actual
+ * elements.  This is either a local function or a function to setup
+ * the problem element key and data pointers which then hands off to
+ * the user function.
+ *
+ * user_compare - User comparison function.  Could be NULL if we are
+ * just using a local comparison function.
+ *
+ * table_p - Associated table being sorted.
+ */
+static void split(void *first_p, void *last_p, compare_t compare,
+                  table_compare_t user_compare, table_t * table_p)
+{
+    void *pivot_p, *left_p, *right_p, *left_last_p, *right_first_p;
+    void *firsts[MAX_SORT_SPLITS], *lasts[MAX_SORT_SPLITS];
+    int split_c = 0;
+
+    for (;;) {
+
+        /* no need to split the list if it is < 2 elements */
+        while (first_p >= last_p) {
+            if (split_c == 0) {
+                /* we are done */
+                return;
+            }
+            split_c--;
+            first_p = firsts[split_c];
+            last_p = lasts[split_c];
+        }
+
+        left_p = first_p;
+        right_p = last_p;
+        pivot_p = first_p;
+
+        do {
+            /* scan from right hand side */
+            while (right_p > left_p
+                   && compare(right_p, pivot_p, user_compare, table_p) > 0)
+                right_p = (char *) right_p - sizeof(table_entry_t *);
+            /* scan from left hand side */
+            while (right_p > left_p
+                   && compare(pivot_p, left_p, user_compare, table_p) >= 0)
+                left_p = (char *) left_p + sizeof(table_entry_t *);
+            /* if the pointers haven't met then swap values */
+            if (right_p > left_p) {
+                /* swap_bytes(left_p, right_p) */
+                table_entry_t *temp;
+
+                temp = *(table_entry_t **) left_p;
+                *(table_entry_t **) left_p = *(table_entry_t **) right_p;
+                *(table_entry_t **) right_p = temp;
+            }
+        } while (right_p > left_p);
+
+        /* now we swap the pivot with the right-hand side */
+        {
+            /* swap_bytes(pivot_p, right_p); */
+            table_entry_t *temp;
+
+            temp = *(table_entry_t **) pivot_p;
+            *(table_entry_t **) pivot_p = *(table_entry_t **) right_p;
+            *(table_entry_t **) right_p = temp;
+        }
+        pivot_p = right_p;
+
+        /* save the section to the right of the pivot in our stack */
+        right_first_p = (char *) pivot_p + sizeof(table_entry_t *);
+        left_last_p = (char *) pivot_p - sizeof(table_entry_t *);
+
+        /* do we need to save the righthand side? */
+        if (right_first_p < last_p) {
+            if (split_c >= MAX_SORT_SPLITS) {
+                /* sanity check here -- we should never get here */
+                abort();
+            }
+            firsts[split_c] = right_first_p;
+            lasts[split_c] = last_p;
+            split_c++;
+        }
+
+        /* do the left hand side of the pivot */
+        /* first_p = first_p */
+        last_p = left_last_p;
+    }
+}
+
+/*************************** exported routines *******************************/
+
+/*
+ * table_t *table_alloc
+ *
+ * DESCRIPTION:
+ *
+ * Allocate a new table structure.
+ *
+ * RETURNS:
+ *
+ * A pointer to the new table structure which must be passed to
+ * table_free to be deallocated.  On error a NULL is returned.
+ *
+ * ARGUMENTS:
+ *
+ * bucket_n - Number of buckets for the hash table.  Our current hash
+ * value works best with base two numbers.  Set to 0 to take the
+ * library default of 1024.
+ *
+ * error_p - Pointer to an integer which, if not NULL, will contain a
+ * table error code.
+ *
+ * malloc_f, realloc_f, free_f - Pointers to malloc(3)-, realloc(3)-
+ * and free(3)-style functions.
+ */
+table_t *table_alloc(const unsigned int bucket_n, int *error_p,
+                     void *(*malloc_f)(size_t size),
+                     void *(*calloc_f)(size_t number, size_t size),
+                     void *(*realloc_f)(void *ptr, size_t size),
+                     void (*free_f)(void *ptr))
+{
+    table_t *table_p = NULL;
+    unsigned int buck_n;
+
+    /* allocate a table structure */
+    if (malloc_f != NULL)
+        table_p = malloc_f(sizeof(table_t));
+    else
+        table_p = malloc(sizeof(table_t));
+    if (table_p == NULL) {
+        if (error_p != NULL)
+            *error_p = TABLE_ERROR_ALLOC;
+        return NULL;
+    }
+
+    if (bucket_n > 0)
+        buck_n = bucket_n;
+    else
+        buck_n = DEFAULT_SIZE;
+    /* allocate the buckets which are NULLed */
+    if (calloc_f != NULL)
+        table_p->ta_buckets = (table_entry_t **)calloc_f(buck_n, sizeof(table_entry_t *));
+    else
+        table_p->ta_buckets = (table_entry_t **)calloc(buck_n, sizeof(table_entry_t *));
+    if (table_p->ta_buckets == NULL) {
+        if (error_p != NULL)
+            *error_p = TABLE_ERROR_ALLOC;
+        if (free_f != NULL)
+            free_f(table_p);
+        else
+            free(table_p);
+        return NULL;
+    }
+
+    /* initialize structure */
+    table_p->ta_magic = TABLE_MAGIC;
+    table_p->ta_flags = 0;
+    table_p->ta_bucket_n = buck_n;
+    table_p->ta_entry_n = 0;
+    table_p->ta_data_align = 0;
+    table_p->ta_linear.tl_magic = 0;
+    table_p->ta_linear.tl_bucket_c = 0;
+    table_p->ta_linear.tl_entry_c = 0;
+    table_p->ta_file_size = 0;
+    table_p->ta_malloc  = malloc_f  != NULL ? malloc_f  : malloc;
+    table_p->ta_calloc  = calloc_f  != NULL ? calloc_f  : calloc;
+    table_p->ta_realloc = realloc_f != NULL ? realloc_f : realloc;
+    table_p->ta_free    = free_f    != NULL ? free_f    : free;
+
+    if (error_p != NULL)
+        *error_p = TABLE_ERROR_NONE;
+    return table_p;
+}
+
+/*
+ * int table_attr
+ *
+ * DESCRIPTION:
+ *
+ * Set the attributes for the table.  The available attributes are
+ * specified at the top of table.h.
+ *
+ * RETURNS:
+ *
+ * Success - TABLE_ERROR_NONE
+ *
+ * Failure - Table error code.
+ *
+ * ARGUMENTS:
+ *
+ * table_p - Pointer to a table structure which we will be altering.
+ *
+ * attr - Attribute(s) that we will be applying to the table.
+ */
+int table_attr(table_t * table_p, const int attr)
+{
+    if (table_p == NULL)
+        return TABLE_ERROR_ARG_NULL;
+    if (table_p->ta_magic != TABLE_MAGIC)
+        return TABLE_ERROR_PNT;
+    table_p->ta_flags = attr;
+
+    return TABLE_ERROR_NONE;
+}
+
+/*
+ * int table_set_data_alignment
+ *
+ * DESCRIPTION:
+ *
+ * Set the alignment for the data in the table.  For data elements
+ * sizeof(long) is recommended unless you use smaller data types
+ * exclusively.
+ *
+ * WARNING: This must be done before any data gets put into the table.
+ *
+ * RETURNS:
+ *
+ * Success - TABLE_ERROR_NONE
+ *
+ * Failure - Table error code.
+ *
+ * ARGUMENTS:
+ *
+ * table_p - Pointer to a table structure which we will be altering.
+ *
+ * alignment - Alignment requested for the data.  Must be a power of
+ * 2.  Set to 0 for none.
+ */
+int table_set_data_alignment(table_t * table_p, const int alignment)
+{
+    int val;
+
+    if (table_p == NULL)
+        return TABLE_ERROR_ARG_NULL;
+    if (table_p->ta_magic != TABLE_MAGIC)
+        return TABLE_ERROR_PNT;
+    if (table_p->ta_entry_n > 0)
+        return TABLE_ERROR_NOT_EMPTY;
+    /* defaults */
+    if (alignment < 2)
+        table_p->ta_data_align = 0;
+    else {
+        /* verify we have a base 2 number */
+        for (val = 2; val < MAX_ALIGNMENT; val *= 2) {
+            if (val == alignment)
+                break;
+        }
+        if (val >= MAX_ALIGNMENT)
+            return TABLE_ERROR_ALIGNMENT;
+        table_p->ta_data_align = alignment;
+    }
+
+    return TABLE_ERROR_NONE;
+}
+
+/*
+ * int table_clear
+ *
+ * DESCRIPTION:
+ *
+ * Clear out and free all elements in a table structure.
+ *
+ * RETURNS:
+ *
+ * Success - TABLE_ERROR_NONE
+ *
+ * Failure - Table error code.
+ *
+ * ARGUMENTS:
+ *
+ * table_p - Table structure pointer that we will be clearing.
+ */
+int table_clear(table_t * table_p)
+{
+    table_entry_t *entry_p, *next_p;
+    table_entry_t **bucket_p, **bounds_p;
+
+    if (table_p == NULL)
+        return TABLE_ERROR_ARG_NULL;
+    if (table_p->ta_magic != TABLE_MAGIC)
+        return TABLE_ERROR_PNT;
+    /* free the table allocation and table structure */
+    bounds_p = table_p->ta_buckets + table_p->ta_bucket_n;
+    for (bucket_p = table_p->ta_buckets; bucket_p < bounds_p; bucket_p++) {
+        for (entry_p = *bucket_p; entry_p != NULL; entry_p = next_p) {
+            /* record the next pointer before we free */
+            next_p = entry_p->te_next_p;
+            table_p->ta_free(entry_p);
+        }
+
+        /* clear the bucket entry after we free its entries */
+        *bucket_p = NULL;
+    }
+
+    /* reset table state info */
+    table_p->ta_entry_n = 0;
+    table_p->ta_linear.tl_magic = 0;
+    table_p->ta_linear.tl_bucket_c = 0;
+    table_p->ta_linear.tl_entry_c = 0;
+
+    return TABLE_ERROR_NONE;
+}
+
+/*
+ * int table_free
+ *
+ * DESCRIPTION:
+ *
+ * Deallocates a table structure.
+ *
+ * RETURNS:
+ *
+ * Success - TABLE_ERROR_NONE
+ *
+ * Failure - Table error code.
+ *
+ * ARGUMENTS:
+ *
+ * table_p - Table structure pointer that we will be freeing.
+ */
+int table_free(table_t * table_p)
+{
+    int ret;
+
+    if (table_p == NULL)
+        return TABLE_ERROR_ARG_NULL;
+    if (table_p->ta_magic != TABLE_MAGIC)
+        return TABLE_ERROR_PNT;
+    ret = table_clear(table_p);
+
+    if (table_p->ta_buckets != NULL)
+        table_p->ta_free(table_p->ta_buckets);
+    table_p->ta_magic = 0;
+    table_p->ta_free(table_p);
+
+    return ret;
+}
+
+/*
+ * int table_insert_kd
+ *
+ * DESCRIPTION:
+ *
+ * Like table_insert except it passes back a pointer to the key and
+ * the data buffers after they have been inserted into the table
+ * structure.
+ *
+ * This routine adds a key/data pair both of which are made up of a
+ * buffer of bytes and an associated size.  Both the key and the data
+ * will be copied into buffers allocated inside the table.  If the key
+ * exists already, the associated data will be replaced if the
+ * overwrite flag is set, otherwise an error is returned.
+ *
+ * NOTE: be very careful changing the values since the table library
+ * provides the pointers to its memory.  The key can _never_ be
+ * changed otherwise you will not find it again.  The data can be
+ * changed but its length can never be altered unless you delete and
+ * re-insert it into the table.
+ *
+ * WARNING: The pointers to the key and data are not in any specific
+ * alignment.  Accessing the key and/or data as an short, integer, or
+ * long pointer directly can cause problems.
+ *
+ * WARNING: Replacing a data cell (not inserting) will cause the table
+ * linked list to be temporarily invalid.  Care must be taken with
+ * multiple threaded programs which are relying on the first/next
+ * linked list to be always valid.
+ *
+ * RETURNS:
+ *
+ * Success - TABLE_ERROR_NONE
+ *
+ * Failure - Table error code.
+ *
+ * ARGUMENTS:
+ *
+ * table_p - Table structure pointer into which we will be inserting a
+ * new key/data pair.
+ *
+ * key_buf - Buffer of bytes of the key that we are inserting.  If you
+ * are storing an (int) as the key (for example) then key_buf should
+ * be a (int *).
+ *
+ * key_size - Size of the key_buf buffer.  If set to < 0 then the
+ * library will do a strlen of key_buf and add 1 for the '\0'.  If you
+ * are storing an (int) as the key (for example) then key_size should
+ * be sizeof(int).
+ *
+ * data_buf - Buffer of bytes of the data that we are inserting.  If
+ * it is NULL then the library will allocate space for the data in the
+ * table without copying in any information.  If data_buf is NULL and
+ * data_size is 0 then the library will associate a NULL data pointer
+ * with the key.  If you are storing a (long) as the data (for
+ * example) then data_buf should be a (long *).
+ *
+ * data_size - Size of the data_buf buffer.  If set to < 0 then the
+ * library will do a strlen of data_buf and add 1 for the '\0'.  If
+ * you are storing an (long) as the key (for example) then key_size
+ * should be sizeof(long).
+ *
+ * key_buf_p - Pointer which, if not NULL, will be set to the address
+ * of the key storage that was allocated in the table.  If you are
+ * storing an (int) as the key (for example) then key_buf_p should be
+ * (int **) i.e. the address of a (int *).
+ *
+ * data_buf_p - Pointer which, if not NULL, will be set to the address
+ * of the data storage that was allocated in the table.  If you are
+ * storing an (long) as the data (for example) then data_buf_p should
+ * be (long **) i.e. the address of a (long *).
+ *
+ * overwrite - Flag which, if set to 1, will allow the overwriting of
+ * the data in the table with the new data if the key already exists
+ * in the table.
+ */
+int table_insert_kd(table_t * table_p,
+                    const void *key_buf, const int key_size,
+                    const void *data_buf, const int data_size,
+                    void **key_buf_p, void **data_buf_p,
+                    const char overwrite_b)
+{
+    int bucket;
+    unsigned int ksize, dsize;
+    table_entry_t *entry_p, *last_p;
+    void *key_copy_p, *data_copy_p;
+
+    /* check the arguments */
+    if (table_p == NULL)
+        return TABLE_ERROR_ARG_NULL;
+    if (table_p->ta_magic != TABLE_MAGIC)
+        return TABLE_ERROR_PNT;
+    if (key_buf == NULL)
+        return TABLE_ERROR_ARG_NULL;
+    /* data_buf can be null but size must be >= 0, if it isn't null size != 0 */
+    if ((data_buf == NULL && data_size < 0)
+        || (data_buf != NULL && data_size == 0))
+        return TABLE_ERROR_SIZE;
+    /* determine sizes of key and data */
+    if (key_size < 0)
+        ksize = strlen((char *) key_buf) + sizeof(char);
+    else
+        ksize = key_size;
+    if (data_size < 0)
+        dsize = strlen((char *) data_buf) + sizeof(char);
+    else
+        dsize = data_size;
+    /* get the bucket number via a hash function */
+    bucket = hash(key_buf, ksize, 0) % table_p->ta_bucket_n;
+
+    /* look for the entry in this bucket, only check keys of the same size */
+    last_p = NULL;
+    for (entry_p = table_p->ta_buckets[bucket];
+         entry_p != NULL;
+         last_p = entry_p, entry_p = entry_p->te_next_p) {
+        if (entry_p->te_key_size == ksize
+            && memcmp(ENTRY_KEY_BUF(entry_p), key_buf, ksize) == 0)
+            break;
+    }
+
+    /* did we find it?  then we are in replace mode. */
+    if (entry_p != NULL) {
+
+        /* can we not overwrite existing data? */
+        if (!overwrite_b) {
+            if (key_buf_p != NULL)
+                *key_buf_p = ENTRY_KEY_BUF(entry_p);
+            if (data_buf_p != NULL) {
+                if (entry_p->te_data_size == 0)
+                    *data_buf_p = NULL;
+                else {
+                    if (table_p->ta_data_align == 0)
+                        *data_buf_p = ENTRY_DATA_BUF(table_p, entry_p);
+                    else
+                        *data_buf_p = entry_data_buf(table_p, entry_p);
+                }
+            }
+            return TABLE_ERROR_OVERWRITE;
+        }
+
+        /* re-alloc entry's data if the new size != the old */
+        if (dsize != entry_p->te_data_size) {
+
+            /*
+             * First we delete it from the list to keep the list whole.
+             * This properly preserves the linked list in case we have a
+             * thread marching through the linked list while we are
+             * inserting.  Maybe this is an unnecessary protection but it
+             * should not harm that much.
+             */
+            if (last_p == NULL)
+                table_p->ta_buckets[bucket] = entry_p->te_next_p;
+            else
+                last_p->te_next_p = entry_p->te_next_p;
+            /*
+             * Realloc the structure which may change its pointer. NOTE:
+             * this may change any previous data_key_p and data_copy_p
+             * pointers.
+             */
+            entry_p = (table_entry_t *) table_p->ta_realloc(entry_p,
+                                                entry_size(table_p,
+                                                       entry_p->te_key_size,
+                                                           dsize));
+            if (entry_p == NULL)
+                return TABLE_ERROR_ALLOC;
+            /* add it back to the front of the list */
+            entry_p->te_data_size = dsize;
+            entry_p->te_next_p = table_p->ta_buckets[bucket];
+            table_p->ta_buckets[bucket] = entry_p;
+        }
+
+        /* copy or replace data in storage */
+        if (dsize > 0) {
+            if (table_p->ta_data_align == 0)
+                data_copy_p = ENTRY_DATA_BUF(table_p, entry_p);
+            else
+                data_copy_p = entry_data_buf(table_p, entry_p);
+            if (data_buf != NULL)
+                memcpy(data_copy_p, data_buf, dsize);
+        }
+        else
+            data_copy_p = NULL;
+        if (key_buf_p != NULL)
+            *key_buf_p = ENTRY_KEY_BUF(entry_p);
+        if (data_buf_p != NULL)
+            *data_buf_p = data_copy_p;
+        /* returning from the section where we were overwriting table data */
+        return TABLE_ERROR_NONE;
+    }
+
+    /*
+     * It is a new entry.
+     */
+
+    /* allocate a new entry */
+    entry_p = (table_entry_t *) table_p->ta_malloc(entry_size(table_p, ksize, dsize));
+    if (entry_p == NULL)
+        return TABLE_ERROR_ALLOC;
+    /* copy key into storage */
+    entry_p->te_key_size = ksize;
+    key_copy_p = ENTRY_KEY_BUF(entry_p);
+    memcpy(key_copy_p, key_buf, ksize);
+
+    /* copy data in */
+    entry_p->te_data_size = dsize;
+    if (dsize > 0) {
+        if (table_p->ta_data_align == 0)
+            data_copy_p = ENTRY_DATA_BUF(table_p, entry_p);
+        else
+            data_copy_p = entry_data_buf(table_p, entry_p);
+        if (data_buf != NULL)
+            memcpy(data_copy_p, data_buf, dsize);
+    }
+    else
+        data_copy_p = NULL;
+    if (key_buf_p != NULL)
+        *key_buf_p = key_copy_p;
+    if (data_buf_p != NULL)
+        *data_buf_p = data_copy_p;
+    /* insert into list, no need to append */
+    entry_p->te_next_p = table_p->ta_buckets[bucket];
+    table_p->ta_buckets[bucket] = entry_p;
+
+    table_p->ta_entry_n++;
+
+    /* do we need auto-adjust? */
+    if (table_p->ta_flags & TABLE_FLAG_AUTO_ADJUST
+        && SHOULD_TABLE_GROW(table_p))
+        return table_adjust(table_p, table_p->ta_entry_n);
+    return TABLE_ERROR_NONE;
+}
+
+/*
+ * int table_insert
+ *
+ * DESCRIPTION:
+ *
+ * Exactly the same as table_insert_kd except it does not pass back a
+ * pointer to the key after they have been inserted into the table
+ * structure.  This is still here for backwards compatibility.
+ *
+ * See table_insert_kd for more information.
+ *
+ * RETURNS:
+ *
+ * Success - TABLE_ERROR_NONE
+ *
+ * Failure - Table error code.
+ *
+ * ARGUMENTS:
+ *
+ * table_p - Table structure pointer into which we will be inserting a
+ * new key/data pair.
+ *
+ * key_buf - Buffer of bytes of the key that we are inserting.  If you
+ * are storing an (int) as the key (for example) then key_buf should
+ * be a (int *).
+ *
+ * key_size - Size of the key_buf buffer.  If set to < 0 then the
+ * library will do a strlen of key_buf and add 1 for the '\0'.  If you
+ * are storing an (int) as the key (for example) then key_size should
+ * be sizeof(int).
+ *
+ * data_buf - Buffer of bytes of the data that we are inserting.  If
+ * it is NULL then the library will allocate space for the data in the
+ * table without copying in any information.  If data_buf is NULL and
+ * data_size is 0 then the library will associate a NULL data pointer
+ * with the key.  If you are storing a (long) as the data (for
+ * example) then data_buf should be a (long *).
+ *
+ * data_size - Size of the data_buf buffer.  If set to < 0 then the
+ * library will do a strlen of data_buf and add 1 for the '\0'.  If
+ * you are storing an (long) as the key (for example) then key_size
+ * should be sizeof(long).
+ *
+ * data_buf_p - Pointer which, if not NULL, will be set to the address
+ * of the data storage that was allocated in the table.  If you are
+ * storing an (long) as the data (for example) then data_buf_p should
+ * be (long **) i.e. the address of a (long *).
+ *
+ * overwrite - Flag which, if set to 1, will allow the overwriting of
+ * the data in the table with the new data if the key already exists
+ * in the table.
+ */
+int table_insert(table_t * table_p,
+                 const void *key_buf, const int key_size,
+                 const void *data_buf, const int data_size,
+                 void **data_buf_p, const char overwrite_b)
+{
+    return table_insert_kd(table_p, key_buf, key_size, data_buf, data_size,
+                           NULL, data_buf_p, overwrite_b);
+}
+
+/*
+ * int table_retrieve
+ *
+ * DESCRIPTION:
+ *
+ * This routine looks up a key made up of a buffer of bytes and an
+ * associated size in the table.  If found then it returns the
+ * associated data information.
+ *
+ * RETURNS:
+ *
+ * Success - TABLE_ERROR_NONE
+ *
+ * Failure - Table error code.
+ *
+ * ARGUMENTS:
+ *
+ * table_p - Table structure pointer into which we will be searching
+ * for the key.
+ *
+ * key_buf - Buffer of bytes of the key that we are searching for.  If
+ * you are looking for an (int) as the key (for example) then key_buf
+ * should be a (int *).
+ *
+ * key_size - Size of the key_buf buffer.  If set to < 0 then the
+ * library will do a strlen of key_buf and add 1 for the '\0'.  If you
+ * are looking for an (int) as the key (for example) then key_size
+ * should be sizeof(int).
+ *
+ * data_buf_p - Pointer which, if not NULL, will be set to the address
+ * of the data storage that was allocated in the table and that is
+ * associated with the key.  If a (long) was stored as the data (for
+ * example) then data_buf_p should be (long **) i.e. the address of a
+ * (long *).
+ *
+ * data_size_p - Pointer to an integer which, if not NULL, will be set
+ * to the size of the data stored in the table that is associated with
+ * the key.
+ */
+int table_retrieve(table_t * table_p,
+                   const void *key_buf, const int key_size,
+                   void **data_buf_p, int *data_size_p)
+{
+    int bucket;
+    unsigned int ksize;
+    table_entry_t *entry_p, **buckets;
+
+    if (table_p == NULL)
+        return TABLE_ERROR_ARG_NULL;
+    if (table_p->ta_magic != TABLE_MAGIC)
+        return TABLE_ERROR_PNT;
+    if (key_buf == NULL)
+        return TABLE_ERROR_ARG_NULL;
+    /* find key size */
+    if (key_size < 0)
+        ksize = strlen((char *) key_buf) + sizeof(char);
+    else
+        ksize = key_size;
+    /* get the bucket number via a has function */
+    bucket = hash(key_buf, ksize, 0) % table_p->ta_bucket_n;
+
+    /* look for the entry in this bucket, only check keys of the same size */
+    buckets = table_p->ta_buckets;
+    for (entry_p = buckets[bucket];
+         entry_p != NULL;
+         entry_p = entry_p->te_next_p) {
+        entry_p = TABLE_POINTER(table_p, table_entry_t *, entry_p);
+        if (entry_p->te_key_size == ksize
+            && memcmp(ENTRY_KEY_BUF(entry_p), key_buf, ksize) == 0)
+            break;
+    }
+
+    /* not found? */
+    if (entry_p == NULL)
+        return TABLE_ERROR_NOT_FOUND;
+    if (data_buf_p != NULL) {
+        if (entry_p->te_data_size == 0)
+            *data_buf_p = NULL;
+        else {
+            if (table_p->ta_data_align == 0)
+                *data_buf_p = ENTRY_DATA_BUF(table_p, entry_p);
+            else
+                *data_buf_p = entry_data_buf(table_p, entry_p);
+        }
+    }
+    if (data_size_p != NULL)
+        *data_size_p = entry_p->te_data_size;
+    return TABLE_ERROR_NONE;
+}
+
+/*
+ * int table_delete
+ *
+ * DESCRIPTION:
+ *
+ * This routine looks up a key made up of a buffer of bytes and an
+ * associated size in the table.  If found then it will be removed
+ * from the table.  The associated data can be passed back to the user
+ * if requested.
+ *
+ * RETURNS:
+ *
+ * Success - TABLE_ERROR_NONE
+ *
+ * Failure - Table error code.
+ *
+ * NOTE: this could be an allocation error if the library is to return
+ * the data to the user.
+ *
+ * ARGUMENTS:
+ *
+ * table_p - Table structure pointer from which we will be deleteing
+ * the key.
+ *
+ * key_buf - Buffer of bytes of the key that we are searching for to
+ * delete.  If you are deleting an (int) key (for example) then
+ * key_buf should be a (int *).
+ *
+ * key_size - Size of the key_buf buffer.  If set to < 0 then the
+ * library will do a strlen of key_buf and add 1 for the '\0'.  If you
+ * are deleting an (int) key (for example) then key_size should be
+ * sizeof(int).
+ *
+ * data_buf_p - Pointer which, if not NULL, will be set to the address
+ * of the data storage that was allocated in the table and that was
+ * associated with the key.  If a (long) was stored as the data (for
+ * example) then data_buf_p should be (long **) i.e. the address of a
+ * (long *).  If a pointer is passed in, the caller is responsible for
+ * freeing it after use.  If data_buf_p is NULL then the library will
+ * free up the data allocation itself.
+ *
+ * data_size_p - Pointer to an integer which, if not NULL, will be set
+ * to the size of the data that was stored in the table and that was
+ * associated with the key.
+ */
+int table_delete(table_t * table_p,
+                 const void *key_buf, const int key_size,
+                 void **data_buf_p, int *data_size_p)
+{
+    int bucket;
+    unsigned int ksize;
+    unsigned char *data_copy_p;
+    table_entry_t *entry_p, *last_p;
+
+    if (table_p == NULL)
+        return TABLE_ERROR_ARG_NULL;
+    if (table_p->ta_magic != TABLE_MAGIC)
+        return TABLE_ERROR_PNT;
+    if (key_buf == NULL)
+        return TABLE_ERROR_ARG_NULL;
+    /* get the key size */
+    if (key_size < 0)
+        ksize = strlen((char *) key_buf) + sizeof(char);
+    else
+        ksize = key_size;
+    /* find our bucket */
+    bucket = hash(key_buf, ksize, 0) % table_p->ta_bucket_n;
+
+    /* look for the entry in this bucket, only check keys of the same size */
+    for (last_p = NULL, entry_p = table_p->ta_buckets[bucket]; entry_p != NULL;
+         last_p = entry_p, entry_p = entry_p->te_next_p) {
+        if (entry_p->te_key_size == ksize
+            && memcmp(ENTRY_KEY_BUF(entry_p), key_buf, ksize) == 0)
+            break;
+    }
+
+    /* did we find it? */
+    if (entry_p == NULL)
+        return TABLE_ERROR_NOT_FOUND;
+    /*
+     * NOTE: we may want to adjust the linear counters here if the entry
+     * we are deleting is the one we are pointing on or is ahead of the
+     * one in the bucket list
+     */
+
+    /* remove entry from the linked list */
+    if (last_p == NULL)
+        table_p->ta_buckets[bucket] = entry_p->te_next_p;
+    else
+        last_p->te_next_p = entry_p->te_next_p;
+    /* free entry */
+    if (data_buf_p != NULL) {
+        if (entry_p->te_data_size == 0)
+            *data_buf_p = NULL;
+        else {
+            /*
+             * if we were storing it compacted, we now need to malloc some
+             * space if the user wants the value after the delete.
+             */
+            *data_buf_p = table_p->ta_malloc(entry_p->te_data_size);
+            if (*data_buf_p == NULL)
+                return TABLE_ERROR_ALLOC;
+            if (table_p->ta_data_align == 0)
+                data_copy_p = ENTRY_DATA_BUF(table_p, entry_p);
+            else
+                data_copy_p = entry_data_buf(table_p, entry_p);
+            memcpy(*data_buf_p, data_copy_p, entry_p->te_data_size);
+        }
+    }
+    if (data_size_p != NULL)
+        *data_size_p = entry_p->te_data_size;
+    table_p->ta_free(entry_p);
+
+    table_p->ta_entry_n--;
+
+    /* do we need auto-adjust down? */
+    if ((table_p->ta_flags & TABLE_FLAG_AUTO_ADJUST)
+        && (table_p->ta_flags & TABLE_FLAG_ADJUST_DOWN)
+        && SHOULD_TABLE_SHRINK(table_p))
+        return table_adjust(table_p, table_p->ta_entry_n);
+    return TABLE_ERROR_NONE;
+}
+
+/*
+ * int table_delete_first
+ *
+ * DESCRIPTION:
+ *
+ * This is like the table_delete routines except it deletes the first
+ * key/data pair in the table instead of an entry corresponding to a
+ * particular key.  The associated key and data information can be
+ * passed back to the user if requested.  This routines is handy to
+ * clear out a table.
+ *
+ * RETURNS:
+ *
+ * Success - TABLE_ERROR_NONE
+ *
+ * Failure - Table error code.
+ *
+ * NOTE: this could be an allocation error if the library is to return
+ * the data to the user.
+ *
+ * ARGUMENTS:
+ *
+ * table_p - Table structure pointer from which we will be deleteing
+ * the first key.
+ *
+ * key_buf_p - Pointer which, if not NULL, will be set to the address
+ * of the storage of the first key that was allocated in the table.
+ * If an (int) was stored as the first key (for example) then
+ * key_buf_p should be (int **) i.e. the address of a (int *).  If a
+ * pointer is passed in, the caller is responsible for freeing it
+ * after use.  If key_buf_p is NULL then the library will free up the
+ * key allocation itself.
+ *
+ * key_size_p - Pointer to an integer which, if not NULL, will be set
+ * to the size of the key that was stored in the table and that was
+ * associated with the key.
+ *
+ * data_buf_p - Pointer which, if not NULL, will be set to the address
+ * of the data storage that was allocated in the table and that was
+ * associated with the key.  If a (long) was stored as the data (for
+ * example) then data_buf_p should be (long **) i.e. the address of a
+ * (long *).  If a pointer is passed in, the caller is responsible for
+ * freeing it after use.  If data_buf_p is NULL then the library will
+ * free up the data allocation itself.
+ *
+ * data_size_p - Pointer to an integer which, if not NULL, will be set
+ * to the size of the data that was stored in the table and that was
+ * associated with the key.
+ */
+int table_delete_first(table_t * table_p,
+                       void **key_buf_p, int *key_size_p,
+                       void **data_buf_p, int *data_size_p)
+{
+    unsigned char *data_copy_p;
+    table_entry_t *entry_p;
+    table_linear_t linear;
+
+    if (table_p == NULL)
+        return TABLE_ERROR_ARG_NULL;
+    if (table_p->ta_magic != TABLE_MAGIC)
+        return TABLE_ERROR_PNT;
+    /* take the first entry */
+    entry_p = first_entry(table_p, &linear);
+    if (entry_p == NULL)
+        return TABLE_ERROR_NOT_FOUND;
+    /*
+     * NOTE: we may want to adjust the linear counters here if the entry
+     * we are deleting is the one we are pointing on or is ahead of the
+     * one in the bucket list
+     */
+
+    /* remove entry from the linked list */
+    table_p->ta_buckets[linear.tl_bucket_c] = entry_p->te_next_p;
+
+    /* free entry */
+    if (key_buf_p != NULL) {
+        if (entry_p->te_key_size == 0)
+            *key_buf_p = NULL;
+        else {
+            /*
+             * if we were storing it compacted, we now need to malloc some
+             * space if the user wants the value after the delete.
+             */
+            *key_buf_p = table_p->ta_malloc(entry_p->te_key_size);
+            if (*key_buf_p == NULL)
+                return TABLE_ERROR_ALLOC;
+            memcpy(*key_buf_p, ENTRY_KEY_BUF(entry_p), entry_p->te_key_size);
+        }
+    }
+    if (key_size_p != NULL)
+        *key_size_p = entry_p->te_key_size;
+    if (data_buf_p != NULL) {
+        if (entry_p->te_data_size == 0)
+            *data_buf_p = NULL;
+        else {
+            /*
+             * if we were storing it compacted, we now need to malloc some
+             * space if the user wants the value after the delete.
+             */
+            *data_buf_p = table_p->ta_malloc(entry_p->te_data_size);
+            if (*data_buf_p == NULL)
+                return TABLE_ERROR_ALLOC;
+            if (table_p->ta_data_align == 0)
+                data_copy_p = ENTRY_DATA_BUF(table_p, entry_p);
+            else
+                data_copy_p = entry_data_buf(table_p, entry_p);
+            memcpy(*data_buf_p, data_copy_p, entry_p->te_data_size);
+        }
+    }
+    if (data_size_p != NULL)
+        *data_size_p = entry_p->te_data_size;
+    table_p->ta_free(entry_p);
+
+    table_p->ta_entry_n--;
+
+    /* do we need auto-adjust down? */
+    if ((table_p->ta_flags & TABLE_FLAG_AUTO_ADJUST)
+        && (table_p->ta_flags & TABLE_FLAG_ADJUST_DOWN)
+        && SHOULD_TABLE_SHRINK(table_p))
+        return table_adjust(table_p, table_p->ta_entry_n);
+    return TABLE_ERROR_NONE;
+}
+
+/*
+ * int table_info
+ *
+ * DESCRIPTION:
+ *
+ * Get some information about a table_p structure.
+ *
+ * RETURNS:
+ *
+ * Success - TABLE_ERROR_NONE
+ *
+ * Failure - Table error code.
+ *
+ * ARGUMENTS:
+ *
+ * table_p - Table structure pointer from which we are getting
+ * information.
+ *
+ * num_buckets_p - Pointer to an integer which, if not NULL, will
+ * contain the number of buckets in the table.
+ *
+ * num_entries_p - Pointer to an integer which, if not NULL, will
+ * contain the number of entries stored in the table.
+ */
+int table_info(table_t * table_p, int *num_buckets_p, int *num_entries_p)
+{
+    if (table_p == NULL)
+        return TABLE_ERROR_ARG_NULL;
+    if (table_p->ta_magic != TABLE_MAGIC)
+        return TABLE_ERROR_PNT;
+    if (num_buckets_p != NULL)
+        *num_buckets_p = table_p->ta_bucket_n;
+    if (num_entries_p != NULL)
+        *num_entries_p = table_p->ta_entry_n;
+    return TABLE_ERROR_NONE;
+}
+
+/*
+ * int table_adjust
+ *
+ * DESCRIPTION:
+ *
+ * Set the number of buckets in a table to a certain value.
+ *
+ * RETURNS:
+ *
+ * Success - TABLE_ERROR_NONE
+ *
+ * Failure - Table error code.
+ *
+ * ARGUMENTS:
+ *
+ * table_p - Table structure pointer of which we are adjusting.
+ *
+ * bucket_n - Number buckets to adjust the table to.  Set to 0 to
+ * adjust the table to its number of entries.
+ */
+int table_adjust(table_t * table_p, const int bucket_n)
+{
+    table_entry_t *entry_p, *next_p;
+    table_entry_t **buckets, **bucket_p, **bounds_p;
+    int bucket;
+    unsigned int buck_n;
+
+    if (table_p == NULL)
+        return TABLE_ERROR_ARG_NULL;
+    if (table_p->ta_magic != TABLE_MAGIC)
+        return TABLE_ERROR_PNT;
+    /*
+     * NOTE: we walk through the entries and rehash them.  If we stored
+     * the hash value as a full int in the table-entry, all we would
+     * have to do is remod it.
+     */
+
+    /* normalize to the number of entries */
+    if (bucket_n == 0)
+        buck_n = table_p->ta_entry_n;
+    else
+        buck_n = bucket_n;
+    /* we must have at least 1 bucket */
+    if (buck_n == 0)
+        buck_n = 1;
+    /* make sure we have somethign to do */
+    if (buck_n == table_p->ta_bucket_n)
+        return TABLE_ERROR_NONE;
+    /* allocate a new bucket list */
+    buckets = (table_entry_t **) table_p->ta_calloc(buck_n, sizeof(table_entry_t *));
+    if (table_p->ta_buckets == NULL)
+        return TABLE_ERROR_ALLOC;
+    /*
+     * run through each of the items in the current table and rehash
+     * them into the newest bucket sizes
+     */
+    bounds_p = table_p->ta_buckets + table_p->ta_bucket_n;
+    for (bucket_p = table_p->ta_buckets; bucket_p < bounds_p; bucket_p++) {
+        for (entry_p = *bucket_p; entry_p != NULL; entry_p = next_p) {
+
+            /* hash the old data into the new table size */
+            bucket = hash(ENTRY_KEY_BUF(entry_p), entry_p->te_key_size, 0) % buck_n;
+
+            /* record the next one now since we overwrite next below */
+            next_p = entry_p->te_next_p;
+
+            /* insert into new list, no need to append */
+            entry_p->te_next_p = buckets[bucket];
+            buckets[bucket] = entry_p;
+
+            /*
+             * NOTE: we may want to adjust the bucket_c linear entry here to
+             * keep it current
+             */
+        }
+        /* remove the old table pointers as we go by */
+        *bucket_p = NULL;
+    }
+
+    /* replace the table buckets with the new ones */
+    table_p->ta_free(table_p->ta_buckets);
+    table_p->ta_buckets = buckets;
+    table_p->ta_bucket_n = buck_n;
+
+    return TABLE_ERROR_NONE;
+}
+
+/*
+ * const char *table_strerror
+ *
+ * DESCRIPTION:
+ *
+ * Return the corresponding string for the error number.
+ *
+ * RETURNS:
+ *
+ * Success - String equivalient of the error.
+ *
+ * Failure - String "invalid error code"
+ *
+ * ARGUMENTS:
+ *
+ * error - Error number that we are converting.
+ */
+const char *table_strerror(const int error)
+{
+    error_str_t *err_p;
+
+    for (err_p = errors; err_p->es_error != 0; err_p++) {
+        if (err_p->es_error == error)
+            return err_p->es_string;
+    }
+
+    return INVALID_ERROR;
+}
+
+/*
+ * int table_type_size
+ *
+ * DESCRIPTION:
+ *
+ * Return the size of the internal table type.
+ *
+ * RETURNS:
+ *
+ * The size of the table_t type.
+ *
+ * ARGUMENTS:
+ *
+ * None.
+ */
+int table_type_size(void)
+{
+    return sizeof(table_t);
+}
+
+/************************* linear access routines ****************************/
+
+/*
+ * int table_first
+ *
+ * DESCRIPTION:
+ *
+ * Find first element in a table and pass back information about the
+ * key/data pair.  If any of the key/data pointers are NULL then they
+ * are ignored.
+ *
+ * NOTE: This function is not reentrant.  More than one thread cannot
+ * be doing a first and next on the same table at the same time.  Use
+ * the table_first_r version below for this.
+ *
+ * RETURNS:
+ *
+ * Success - TABLE_ERROR_NONE
+ *
+ * Failure - Table error code.
+ *
+ * ARGUMENTS:
+ *
+ * table_p - Table structure pointer from which we are getting the
+ * first element.
+ *
+ * key_buf_p - Pointer which, if not NULL, will be set to the address
+ * of the storage of the first key that is allocated in the table.  If
+ * an (int) is stored as the first key (for example) then key_buf_p
+ * should be (int **) i.e. the address of a (int *).
+ *
+ * key_size_p - Pointer to an integer which, if not NULL, will be set
+ * to the size of the key that is stored in the table and that is
+ * associated with the first key.
+ *
+ * data_buf_p - Pointer which, if not NULL, will be set to the address
+ * of the data storage that is allocated in the table and that is
+ * associated with the first key.  If a (long) is stored as the data
+ * (for example) then data_buf_p should be (long **) i.e. the address
+ * of a (long *).
+ *
+ * data_size_p - Pointer to an integer which, if not NULL, will be set
+ * to the size of the data that is stored in the table and that is
+ * associated with the first key.
+ */
+int table_first(table_t * table_p,
+                void **key_buf_p, int *key_size_p,
+                void **data_buf_p, int *data_size_p)
+{
+    table_entry_t *entry_p;
+
+    if (table_p == NULL)
+        return TABLE_ERROR_ARG_NULL;
+    if (table_p->ta_magic != TABLE_MAGIC)
+        return TABLE_ERROR_PNT;
+    /* initialize our linear magic number */
+    table_p->ta_linear.tl_magic = LINEAR_MAGIC;
+
+    entry_p = first_entry(table_p, &table_p->ta_linear);
+    if (entry_p == NULL)
+        return TABLE_ERROR_NOT_FOUND;
+    if (key_buf_p != NULL)
+        *key_buf_p = ENTRY_KEY_BUF(entry_p);
+    if (key_size_p != NULL)
+        *key_size_p = entry_p->te_key_size;
+    if (data_buf_p != NULL) {
+        if (entry_p->te_data_size == 0)
+            *data_buf_p = NULL;
+        else {
+            if (table_p->ta_data_align == 0)
+                *data_buf_p = ENTRY_DATA_BUF(table_p, entry_p);
+            else
+                *data_buf_p = entry_data_buf(table_p, entry_p);
+        }
+    }
+    if (data_size_p != NULL)
+        *data_size_p = entry_p->te_data_size;
+    return TABLE_ERROR_NONE;
+}
+
+/*
+ * int table_next
+ *
+ * DESCRIPTION:
+ *
+ * Find the next element in a table and pass back information about
+ * the key/data pair.  If any of the key/data pointers are NULL then
+ * they are ignored.
+ *
+ * NOTE: This function is not reentrant.  More than one thread cannot
+ * be doing a first and next on the same table at the same time.  Use
+ * the table_next_r version below for this.
+ *
+ * RETURNS:
+ *
+ * Success - TABLE_ERROR_NONE
+ *
+ * Failure - Table error code.
+ *
+ * ARGUMENTS:
+ *
+ * table_p - Table structure pointer from which we are getting the
+ * next element.
+ *
+ * key_buf_p - Pointer which, if not NULL, will be set to the address
+ * of the storage of the next key that is allocated in the table.  If
+ * an (int) is stored as the next key (for example) then key_buf_p
+ * should be (int **) i.e. the address of a (int *).
+ *
+ * key_size_p - Pointer to an integer which, if not NULL, will be set
+ * to the size of the key that is stored in the table and that is
+ * associated with the next key.
+ *
+ * data_buf_p - Pointer which, if not NULL, will be set to the address
+ * of the data storage that is allocated in the table and that is
+ * associated with the next key.  If a (long) is stored as the data
+ * (for example) then data_buf_p should be (long **) i.e. the address
+ * of a (long *).
+ *
+ * data_size_p - Pointer to an integer which, if not NULL, will be set
+ * to the size of the data that is stored in the table and that is
+ * associated with the next key.
+ */
+int table_next(table_t * table_p,
+               void **key_buf_p, int *key_size_p,
+               void **data_buf_p, int *data_size_p)
+{
+    table_entry_t *entry_p;
+    int error;
+
+    if (table_p == NULL)
+        return TABLE_ERROR_ARG_NULL;
+    if (table_p->ta_magic != TABLE_MAGIC)
+        return TABLE_ERROR_PNT;
+    if (table_p->ta_linear.tl_magic != LINEAR_MAGIC)
+        return TABLE_ERROR_LINEAR;
+    /* move to the next entry */
+    entry_p = next_entry(table_p, &table_p->ta_linear, &error);
+    if (entry_p == NULL)
+        return error;
+    if (key_buf_p != NULL)
+        *key_buf_p = ENTRY_KEY_BUF(entry_p);
+    if (key_size_p != NULL)
+        *key_size_p = entry_p->te_key_size;
+    if (data_buf_p != NULL) {
+        if (entry_p->te_data_size == 0)
+            *data_buf_p = NULL;
+        else {
+            if (table_p->ta_data_align == 0)
+                *data_buf_p = ENTRY_DATA_BUF(table_p, entry_p);
+            else
+                *data_buf_p = entry_data_buf(table_p, entry_p);
+        }
+    }
+    if (data_size_p != NULL)
+        *data_size_p = entry_p->te_data_size;
+    return TABLE_ERROR_NONE;
+}
+
+/*
+ * int table_this
+ *
+ * DESCRIPTION:
+ *
+ * Find the current element in a table and pass back information about
+ * the key/data pair.  If any of the key/data pointers are NULL then
+ * they are ignored.
+ *
+ * NOTE: This function is not reentrant.  Use the table_current_r
+ * version below.
+ *
+ * RETURNS:
+ *
+ * Success - TABLE_ERROR_NONE
+ *
+ * Failure - Table error code.
+ *
+ * ARGUMENTS:
+ *
+ * table_p - Table structure pointer from which we are getting the
+ * current element.
+ *
+ * key_buf_p - Pointer which, if not NULL, will be set to the address
+ * of the storage of the current key that is allocated in the table.
+ * If an (int) is stored as the current key (for example) then
+ * key_buf_p should be (int **) i.e. the address of a (int *).
+ *
+ * key_size_p - Pointer to an integer which, if not NULL, will be set
+ * to the size of the key that is stored in the table and that is
+ * associated with the current key.
+ *
+ * data_buf_p - Pointer which, if not NULL, will be set to the address
+ * of the data storage that is allocated in the table and that is
+ * associated with the current key.  If a (long) is stored as the data
+ * (for example) then data_buf_p should be (long **) i.e. the address
+ * of a (long *).
+ *
+ * data_size_p - Pointer to an integer which, if not NULL, will be set
+ * to the size of the data that is stored in the table and that is
+ * associated with the current key.
+ */
+int table_this(table_t * table_p,
+               void **key_buf_p, int *key_size_p,
+               void **data_buf_p, int *data_size_p)
+{
+    table_entry_t *entry_p = NULL;
+    int entry_c;
+
+    if (table_p == NULL)
+        return TABLE_ERROR_ARG_NULL;
+    if (table_p->ta_magic != TABLE_MAGIC)
+        return TABLE_ERROR_PNT;
+    if (table_p->ta_linear.tl_magic != LINEAR_MAGIC)
+        return TABLE_ERROR_LINEAR;
+    /* if we removed an item that shorted the bucket list, we may get this */
+    if (table_p->ta_linear.tl_bucket_c >= table_p->ta_bucket_n) {
+        /*
+         * NOTE: this might happen if we delete an item which shortens the
+         * table bucket numbers.
+         */
+        return TABLE_ERROR_NOT_FOUND;
+    }
+
+    /* find the entry which is the nth in the list */
+    entry_p = table_p->ta_buckets[table_p->ta_linear.tl_bucket_c];
+    /* NOTE: we swap the order here to be more efficient */
+    for (entry_c = table_p->ta_linear.tl_entry_c; entry_c > 0; entry_c--) {
+        /* did we reach the end of the list? */
+        if (entry_p == NULL)
+            break;
+        entry_p = TABLE_POINTER(table_p, table_entry_t *, entry_p)->te_next_p;
+    }
+
+    /* is this a NOT_FOUND or a LINEAR error */
+    if (entry_p == NULL)
+        return TABLE_ERROR_NOT_FOUND;
+    if (key_buf_p != NULL)
+        *key_buf_p = ENTRY_KEY_BUF(entry_p);
+    if (key_size_p != NULL)
+        *key_size_p = entry_p->te_key_size;
+    if (data_buf_p != NULL) {
+        if (entry_p->te_data_size == 0)
+            *data_buf_p = NULL;
+        else {
+            if (table_p->ta_data_align == 0)
+                *data_buf_p = ENTRY_DATA_BUF(table_p, entry_p);
+            else
+                *data_buf_p = entry_data_buf(table_p, entry_p);
+        }
+    }
+    if (data_size_p != NULL)
+        *data_size_p = entry_p->te_data_size;
+    return TABLE_ERROR_NONE;
+}
+
+/*
+ * int table_first_r
+ *
+ * DESCRIPTION:
+ *
+ * Reetrant version of the table_first routine above.  Find first
+ * element in a table and pass back information about the key/data
+ * pair.  If any of the key/data pointers are NULL then they are
+ * ignored.
+ *
+ * RETURNS:
+ *
+ * Success - TABLE_ERROR_NONE
+ *
+ * Failure - Table error code.
+ *
+ * ARGUMENTS:
+ *
+ * table_p - Table structure pointer from which we are getting the
+ * first element.
+ *
+ * linear_p - Pointer to a table linear structure which is initialized
+ * here.  The same pointer should then be passed to table_next_r
+ * below.
+ *
+ * key_buf_p - Pointer which, if not NULL, will be set to the address
+ * of the storage of the first key that is allocated in the table.  If
+ * an (int) is stored as the first key (for example) then key_buf_p
+ * should be (int **) i.e. the address of a (int *).
+ *
+ * key_size_p - Pointer to an integer which, if not NULL, will be set
+ * to the size of the key that is stored in the table and that is
+ * associated with the first key.
+ *
+ * data_buf_p - Pointer which, if not NULL, will be set to the address
+ * of the data storage that is allocated in the table and that is
+ * associated with the first key.  If a (long) is stored as the data
+ * (for example) then data_buf_p should be (long **) i.e. the address
+ * of a (long *).
+ *
+ * data_size_p - Pointer to an integer which, if not NULL, will be set
+ * to the size of the data that is stored in the table and that is
+ * associated with the first key.
+ */
+int table_first_r(table_t * table_p, table_linear_t * linear_p,
+                  void **key_buf_p, int *key_size_p,
+                  void **data_buf_p, int *data_size_p)
+{
+    table_entry_t *entry_p;
+
+    if (table_p == NULL)
+        return TABLE_ERROR_ARG_NULL;
+    if (table_p->ta_magic != TABLE_MAGIC)
+        return TABLE_ERROR_PNT;
+    if (linear_p == NULL)
+        return TABLE_ERROR_ARG_NULL;
+    /* initialize our linear magic number */
+    linear_p->tl_magic = LINEAR_MAGIC;
+
+    entry_p = first_entry(table_p, linear_p);
+    if (entry_p == NULL)
+        return TABLE_ERROR_NOT_FOUND;
+    if (key_buf_p != NULL)
+        *key_buf_p = ENTRY_KEY_BUF(entry_p);
+    if (key_size_p != NULL)
+        *key_size_p = entry_p->te_key_size;
+    if (data_buf_p != NULL) {
+        if (entry_p->te_data_size == 0)
+            *data_buf_p = NULL;
+        else {
+            if (table_p->ta_data_align == 0)
+                *data_buf_p = ENTRY_DATA_BUF(table_p, entry_p);
+            else
+                *data_buf_p = entry_data_buf(table_p, entry_p);
+        }
+    }
+    if (data_size_p != NULL)
+        *data_size_p = entry_p->te_data_size;
+    return TABLE_ERROR_NONE;
+}
+
+/*
+ * int table_next_r
+ *
+ * DESCRIPTION:
+ *
+ * Reetrant version of the table_next routine above.  Find next
+ * element in a table and pass back information about the key/data
+ * pair.  If any of the key/data pointers are NULL then they are
+ * ignored.
+ *
+ * RETURNS:
+ *
+ * Success - TABLE_ERROR_NONE
+ *
+ * Failure - Table error code.
+ *
+ * ARGUMENTS:
+ *
+ * table_p - Table structure pointer from which we are getting the
+ * next element.
+ *
+ * linear_p - Pointer to a table linear structure which is incremented
+ * here.  The same pointer must have been passed to table_first_r
+ * first so that it can be initialized.
+ *
+ * key_buf_p - Pointer which, if not NULL, will be set to the address
+ * of the storage of the next key that is allocated in the table.  If
+ * an (int) is stored as the next key (for example) then key_buf_p
+ * should be (int **) i.e. the address of a (int *).
+ *
+ * key_size_p - Pointer to an integer which, if not NULL will be set
+ * to the size of the key that is stored in the table and that is
+ * associated with the next key.
+ *
+ * data_buf_p - Pointer which, if not NULL, will be set to the address
+ * of the data storage that is allocated in the table and that is
+ * associated with the next key.  If a (long) is stored as the data
+ * (for example) then data_buf_p should be (long **) i.e. the address
+ * of a (long *).
+ *
+ * data_size_p - Pointer to an integer which, if not NULL, will be set
+ * to the size of the data that is stored in the table and that is
+ * associated with the next key.
+ */
+int table_next_r(table_t * table_p, table_linear_t * linear_p,
+                 void **key_buf_p, int *key_size_p,
+                 void **data_buf_p, int *data_size_p)
+{
+    table_entry_t *entry_p;
+    int error;
+
+    if (table_p == NULL)
+        return TABLE_ERROR_ARG_NULL;
+    if (table_p->ta_magic != TABLE_MAGIC)
+        return TABLE_ERROR_PNT;
+    if (linear_p == NULL)
+        return TABLE_ERROR_ARG_NULL;
+    if (linear_p->tl_magic != LINEAR_MAGIC)
+        return TABLE_ERROR_LINEAR;
+    /* move to the next entry */
+    entry_p = next_entry(table_p, linear_p, &error);
+    if (entry_p == NULL)
+        return error;
+    if (key_buf_p != NULL)
+        *key_buf_p = ENTRY_KEY_BUF(entry_p);
+    if (key_size_p != NULL)
+        *key_size_p = entry_p->te_key_size;
+    if (data_buf_p != NULL) {
+        if (entry_p->te_data_size == 0)
+            *data_buf_p = NULL;
+        else {
+            if (table_p->ta_data_align == 0)
+                *data_buf_p = ENTRY_DATA_BUF(table_p, entry_p);
+            else
+                *data_buf_p = entry_data_buf(table_p, entry_p);
+        }
+    }
+    if (data_size_p != NULL)
+        *data_size_p = entry_p->te_data_size;
+    return TABLE_ERROR_NONE;
+}
+
+/*
+ * int table_this_r
+ *
+ * DESCRIPTION:
+ *
+ * Reetrant version of the table_this routine above.  Find current
+ * element in a table and pass back information about the key/data
+ * pair.  If any of the key/data pointers are NULL then they are
+ * ignored.
+ *
+ * RETURNS:
+ *
+ * Success - TABLE_ERROR_NONE
+ *
+ * Failure - Table error code.
+ *
+ * ARGUMENTS:
+ *
+ * table_p - Table structure pointer from which we are getting the
+ * current element.
+ *
+ * linear_p - Pointer to a table linear structure which is accessed
+ * here.  The same pointer must have been passed to table_first_r
+ * first so that it can be initialized.
+ *
+ * key_buf_p - Pointer which, if not NULL, will be set to the address
+ * of the storage of the current key that is allocated in the table.
+ * If an (int) is stored as the current key (for example) then
+ * key_buf_p should be (int **) i.e. the address of a (int *).
+ *
+ * key_size_p - Pointer to an integer which, if not NULL, will be set
+ * to the size of the key that is stored in the table and that is
+ * associated with the current key.
+ *
+ * data_buf_p - Pointer which, if not NULL, will be set to the address
+ * of the data storage that is allocated in the table and that is
+ * associated with the current key.  If a (long) is stored as the data
+ * (for example) then data_buf_p should be (long **) i.e. the address
+ * of a (long *).
+ *
+ * data_size_p - Pointer to an integer which, if not NULL, will be set
+ * to the size of the data that is stored in the table and that is
+ * associated with the current key.
+ */
+int table_this_r(table_t * table_p, table_linear_t * linear_p,
+                 void **key_buf_p, int *key_size_p,
+                 void **data_buf_p, int *data_size_p)
+{
+    table_entry_t *entry_p;
+    int entry_c;
+
+    if (table_p == NULL)
+        return TABLE_ERROR_ARG_NULL;
+    if (table_p->ta_magic != TABLE_MAGIC)
+        return TABLE_ERROR_PNT;
+    if (linear_p->tl_magic != LINEAR_MAGIC)
+        return TABLE_ERROR_LINEAR;
+    /* if we removed an item that shorted the bucket list, we may get this */
+    if (linear_p->tl_bucket_c >= table_p->ta_bucket_n) {
+        /*
+         * NOTE: this might happen if we delete an item which shortens the
+         * table bucket numbers.
+         */
+        return TABLE_ERROR_NOT_FOUND;
+    }
+
+    /* find the entry which is the nth in the list */
+    for (entry_c = linear_p->tl_entry_c,
+         entry_p = table_p->ta_buckets[linear_p->tl_bucket_c];
+         entry_p != NULL && entry_c > 0;
+         entry_c--, entry_p = TABLE_POINTER(table_p, table_entry_t *,
+                                            entry_p)->te_next_p) {
+    }
+
+    if (entry_p == NULL)
+        return TABLE_ERROR_NOT_FOUND;
+    if (key_buf_p != NULL)
+        *key_buf_p = ENTRY_KEY_BUF(entry_p);
+    if (key_size_p != NULL)
+        *key_size_p = entry_p->te_key_size;
+    if (data_buf_p != NULL) {
+        if (entry_p->te_data_size == 0)
+            *data_buf_p = NULL;
+        else {
+            if (table_p->ta_data_align == 0)
+                *data_buf_p = ENTRY_DATA_BUF(table_p, entry_p);
+            else
+                *data_buf_p = entry_data_buf(table_p, entry_p);
+        }
+    }
+    if (data_size_p != NULL)
+        *data_size_p = entry_p->te_data_size;
+    return TABLE_ERROR_NONE;
+}
+
+/******************************* file routines *******************************/
+
+/*
+ * int table_read
+ *
+ * DESCRIPTION:
+ *
+ * Read in a table from a file that had been written to disk earlier
+ * via table_write.
+ *
+ * RETURNS:
+ *
+ * Success - Pointer to the new table structure which must be passed
+ * to table_free to be deallocated.
+ *
+ * Failure - NULL
+ *
+ * ARGUMENTS:
+ *
+ * path - Table file to read in.
+ *
+ * error_p - Pointer to an integer which, if not NULL, will contain a
+ * table error code.
+ */
+table_t *table_read(const char *path, int *error_p,
+                    void *(*malloc_f)(size_t size),
+                    void *(*calloc_f)(size_t number, size_t size),
+                    void *(*realloc_f)(void *ptr, size_t size),
+                    void (*free_f)(void *ptr))
+{
+    unsigned int size;
+    int fd, ent_size;
+    FILE *infile;
+    table_entry_t entry, **bucket_p, *entry_p = NULL, *last_p;
+    unsigned long pos;
+    table_t *table_p;
+
+    /* open the file */
+    fd = open(path, O_RDONLY, 0);
+    if (fd < 0) {
+        if (error_p != NULL)
+            *error_p = TABLE_ERROR_OPEN;
+        return NULL;
+    }
+
+    /* allocate a table structure */
+    if (malloc_f != NULL)
+        table_p = malloc_f(sizeof(table_t));
+    else
+        table_p = malloc(sizeof(table_t));
+    if (table_p == NULL) {
+        if (error_p != NULL)
+            *error_p = TABLE_ERROR_ALLOC;
+        return NULL;
+    }
+
+    /* now open the fd to get buffered i/o */
+    infile = fdopen(fd, "r");
+    if (infile == NULL) {
+        if (error_p != NULL)
+            *error_p = TABLE_ERROR_OPEN;
+        return NULL;
+    }
+
+    /* read the main table struct */
+    if (fread(table_p, sizeof(table_t), 1, infile) != 1) {
+        if (error_p != NULL)
+            *error_p = TABLE_ERROR_READ;
+        if (free_f != NULL)
+            free_f(table_p);
+        else
+            free(table_p);
+        return NULL;
+    }
+    table_p->ta_file_size = 0;
+
+    table_p->ta_malloc  = malloc_f  != NULL ? malloc_f  : malloc;
+    table_p->ta_calloc  = calloc_f  != NULL ? calloc_f  : calloc;
+    table_p->ta_realloc = realloc_f != NULL ? realloc_f : realloc;
+    table_p->ta_free    = free_f    != NULL ? free_f    : free;
+
+    /* is the file contain bad info or maybe another system type? */
+    if (table_p->ta_magic != TABLE_MAGIC) {
+        if (error_p != NULL)
+            *error_p = TABLE_ERROR_PNT;
+        return NULL;
+    }
+
+    /* allocate the buckets */
+    table_p->ta_buckets = (table_entry_t **)table_p->ta_calloc(table_p->ta_bucket_n, sizeof(table_entry_t *));
+    if (table_p->ta_buckets == NULL) {
+        if (error_p != NULL)
+            *error_p = TABLE_ERROR_ALLOC;
+        table_p->ta_free(table_p);
+        return NULL;
+    }
+
+    if (fread(table_p->ta_buckets, sizeof(table_entry_t *), table_p->ta_bucket_n,
+              infile) != (size_t) table_p->ta_bucket_n) {
+        if (error_p != NULL)
+            *error_p = TABLE_ERROR_READ;
+        table_p->ta_free(table_p->ta_buckets);
+        table_p->ta_free(table_p);
+        return NULL;
+    }
+
+    /* read in the entries */
+    for (bucket_p = table_p->ta_buckets;
+         bucket_p < table_p->ta_buckets + table_p->ta_bucket_n;
+         bucket_p++) {
+
+        /* skip null buckets */
+        if (*bucket_p == NULL)
+            continue;
+        /* run through the entry list */
+        last_p = NULL;
+        for (pos = *(unsigned long *) bucket_p;;
+             pos = (unsigned long) entry_p->te_next_p) {
+
+            /* read in the entry */
+            if (fseek(infile, pos, SEEK_SET) != 0) {
+                if (error_p != NULL)
+                    *error_p = TABLE_ERROR_SEEK;
+                table_p->ta_free(table_p->ta_buckets);
+                if (entry_p != NULL)
+                    table_p->ta_free(entry_p);
+                table_p->ta_free(table_p);
+                /* the other table elements will not be freed */
+                return NULL;
+            }
+            if (fread(&entry, sizeof(struct table_shell_st), 1, infile) != 1) {
+                if (error_p != NULL)
+                    *error_p = TABLE_ERROR_READ;
+                table_p->ta_free(table_p->ta_buckets);
+                if (entry_p != NULL)
+                    table_p->ta_free(entry_p);
+                table_p->ta_free(table_p);
+                /* the other table elements will not be freed */
+                return NULL;
+            }
+
+            /* make a new entry */
+            ent_size = entry_size(table_p, entry.te_key_size, entry.te_data_size);
+            entry_p = (table_entry_t *)table_p->ta_malloc(ent_size);
+            if (entry_p == NULL) {
+                if (error_p != NULL)
+                    *error_p = TABLE_ERROR_ALLOC;
+                table_p->ta_free(table_p->ta_buckets);
+                table_p->ta_free(table_p);
+                /* the other table elements will not be freed */
+                return NULL;
+            }
+            entry_p->te_key_size = entry.te_key_size;
+            entry_p->te_data_size = entry.te_data_size;
+            entry_p->te_next_p = entry.te_next_p;
+
+            if (last_p == NULL)
+                *bucket_p = entry_p;
+            else
+                last_p->te_next_p = entry_p;
+            /* determine how much more we have to read */
+            size = ent_size - sizeof(struct table_shell_st);
+            if (fread(ENTRY_KEY_BUF(entry_p), sizeof(char), size, infile) != size) {
+                if (error_p != NULL)
+                    *error_p = TABLE_ERROR_READ;
+                table_p->ta_free(table_p->ta_buckets);
+                table_p->ta_free(entry_p);
+                table_p->ta_free(table_p);
+                /* the other table elements will not be freed */
+                return NULL;
+            }
+
+            /* we are done if the next pointer is null */
+            if (entry_p->te_next_p == (unsigned long) 0)
+                break;
+            last_p = entry_p;
+        }
+    }
+
+    (void) fclose(infile);
+
+    if (error_p != NULL)
+        *error_p = TABLE_ERROR_NONE;
+    return table_p;
+}
+
+/*
+ * int table_write
+ *
+ * DESCRIPTION:
+ *
+ * Write a table from memory to file.
+ *
+ * RETURNS:
+ *
+ * Success - TABLE_ERROR_NONE
+ *
+ * Failure - Table error code.
+ *
+ * ARGUMENTS:
+ *
+ * table_p - Pointer to the table that we are writing to the file.
+ *
+ * path - Table file to write out to.
+ *
+ * mode - Mode of the file.  This argument is passed on to open when
+ * the file is created.
+ */
+int table_write(const table_t * table_p, const char *path, const int mode)
+{
+    int fd, rem, ent_size;
+    unsigned int bucket_c;
+    unsigned long size;
+    table_entry_t *entry_p, **buckets, **bucket_p, *next_p;
+    table_t tmain;
+    FILE *outfile;
+
+    if (table_p == NULL)
+        return TABLE_ERROR_ARG_NULL;
+    if (table_p->ta_magic != TABLE_MAGIC)
+        return TABLE_ERROR_PNT;
+    fd = open(path, O_WRONLY | O_CREAT, mode);
+    if (fd < 0)
+        return TABLE_ERROR_OPEN;
+    outfile = fdopen(fd, "w");
+    if (outfile == NULL)
+        return TABLE_ERROR_OPEN;
+    /* allocate a block of sizes for each bucket */
+    buckets = (table_entry_t **) table_p->ta_malloc(sizeof(table_entry_t *) *
+                                        table_p->ta_bucket_n);
+    if (buckets == NULL)
+        return TABLE_ERROR_ALLOC;
+    /* make a copy of the tmain struct */
+    tmain = *table_p;
+
+    /* start counting the bytes */
+    size = 0;
+    size += sizeof(table_t);
+
+    /* buckets go right after tmain struct */
+    tmain.ta_buckets = (table_entry_t **) size;
+    size += sizeof(table_entry_t *) * table_p->ta_bucket_n;
+
+    /* run through and count the buckets */
+    for (bucket_c = 0; bucket_c < table_p->ta_bucket_n; bucket_c++) {
+        bucket_p = table_p->ta_buckets + bucket_c;
+        if (*bucket_p == NULL) {
+            buckets[bucket_c] = NULL;
+            continue;
+        }
+        buckets[bucket_c] = (table_entry_t *) size;
+        for (entry_p = *bucket_p; entry_p != NULL; entry_p = entry_p->te_next_p) {
+            size += entry_size(table_p, entry_p->te_key_size, entry_p->te_data_size);
+            /*
+             * We now have to round the file to the nearest long so the
+             * mmaping of the longs in the entry structs will work.
+             */
+            rem = size & (sizeof(long) - 1);
+            if (rem > 0)
+                size += sizeof(long) - rem;
+        }
+    }
+    /* add a \0 at the end to fill the last section */
+    size++;
+
+    /* set the tmain fields */
+    tmain.ta_linear.tl_magic = 0;
+    tmain.ta_linear.tl_bucket_c = 0;
+    tmain.ta_linear.tl_entry_c = 0;
+    tmain.ta_file_size = size;
+
+    /*
+     * Now we can start the writing because we got the bucket offsets.
+     */
+
+    /* write the tmain table struct */
+    size = 0;
+    if (fwrite(&tmain, sizeof(table_t), 1, outfile) != 1) {
+        table_p->ta_free(buckets);
+        return TABLE_ERROR_WRITE;
+    }
+    size += sizeof(table_t);
+    if (fwrite(buckets, sizeof(table_entry_t *), table_p->ta_bucket_n,
+               outfile) != (size_t) table_p->ta_bucket_n) {
+        table_p->ta_free(buckets);
+        return TABLE_ERROR_WRITE;
+    }
+    size += sizeof(table_entry_t *) * table_p->ta_bucket_n;
+
+    /* write out the entries */
+    for (bucket_p = table_p->ta_buckets;
+         bucket_p < table_p->ta_buckets + table_p->ta_bucket_n;
+         bucket_p++) {
+        for (entry_p = *bucket_p; entry_p != NULL; entry_p = entry_p->te_next_p) {
+
+            ent_size = entry_size(table_p, entry_p->te_key_size,
+                                  entry_p->te_data_size);
+            size += ent_size;
+            /* round to nearest long here so we can write copy */
+            rem = size & (sizeof(long) - 1);
+            if (rem > 0)
+                size += sizeof(long) - rem;
+            next_p = entry_p->te_next_p;
+            if (next_p != NULL)
+                entry_p->te_next_p = (table_entry_t *) size;
+            /* now write to disk */
+            if (fwrite(entry_p, ent_size, 1, outfile) != 1) {
+                table_p->ta_free(buckets);
+                return TABLE_ERROR_WRITE;
+            }
+
+            /* restore the next pointer */
+            if (next_p != NULL)
+                entry_p->te_next_p = next_p;
+            /* now write the padding information */
+            if (rem > 0) {
+                rem = sizeof(long) - rem;
+                /*
+                 * NOTE: this won't leave fseek'd space at the end but we
+                 * don't care there because there is no accessed memory
+                 * afterwards.  We write 1 \0 at the end to make sure.
+                 */
+                if (fseek(outfile, rem, SEEK_CUR) != 0) {
+                    table_p->ta_free(buckets);
+                    return TABLE_ERROR_SEEK;
+                }
+            }
+        }
+    }
+    /*
+     * Write a \0 at the end of the file to make sure that the last
+     * fseek filled with nulls.
+     */
+    (void) fputc('\0', outfile);
+
+    (void) fclose(outfile);
+    table_p->ta_free(buckets);
+
+    return TABLE_ERROR_NONE;
+}
+
+/******************************** table order ********************************/
+
+/*
+ * table_entry_t *table_order
+ *
+ * DESCRIPTION:
+ *
+ * Order a table by building an array of table entry pointers and then
+ * sorting this array using the qsort function.  To retrieve the
+ * sorted entries, you can then use the table_entry routine to access
+ * each entry in order.
+ *
+ * NOTE: This routine is now thread safe in that two table_order calls
+ * can now happen at the same time, even on the same table.
+ *
+ * RETURNS:
+ *
+ * An allocated list of entry pointers which must be freed later.
+ * Returns null on error.
+ *
+ * ARGUMENTS:
+ *
+ * table_p - Pointer to the table that we are ordering.
+ *
+ * compare - Comparison function defined by the user.  Its definition
+ * is at the top of the table.h file.  If this is NULL then it will
+ * order the table my memcmp-ing the keys.
+ *
+ * num_entries_p - Pointer to an integer which, if not NULL, will
+ * contain the number of entries in the returned entry pointer array.
+ *
+ * error_p - Pointer to an integer which, if not NULL, will contain a
+ * table error code.
+ */
+table_entry_t **table_order(table_t * table_p, table_compare_t compare,
+                            int *num_entries_p, int *error_p)
+{
+    table_entry_t *entry_p, **entries, **entries_p;
+    table_linear_t linear;
+    compare_t comp_func;
+    int error;
+
+    if (table_p == NULL) {
+        if (error_p != NULL)
+            *error_p = TABLE_ERROR_ARG_NULL;
+        return NULL;
+    }
+    if (table_p->ta_magic != TABLE_MAGIC) {
+        if (error_p != NULL)
+            *error_p = TABLE_ERROR_PNT;
+        return NULL;
+    }
+
+    /* there must be at least 1 element in the table for this to work */
+    if (table_p->ta_entry_n == 0) {
+        if (error_p != NULL)
+            *error_p = TABLE_ERROR_EMPTY;
+        return NULL;
+    }
+
+    entries = (table_entry_t **) table_p->ta_malloc(table_p->ta_entry_n *
+                                        sizeof(table_entry_t *));
+    if (entries == NULL) {
+        if (error_p != NULL)
+            *error_p = TABLE_ERROR_ALLOC;
+        return NULL;
+    }
+
+    /* get a pointer to all entries */
+    entry_p = first_entry(table_p, &linear);
+    if (entry_p == NULL) {
+        if (error_p != NULL)
+            *error_p = TABLE_ERROR_NOT_FOUND;
+        return NULL;
+    }
+
+    /* add all of the entries to the array */
+    for (entries_p = entries;
+         entry_p != NULL;
+         entry_p = next_entry(table_p, &linear, &error))
+        *entries_p++ = entry_p;
+    if (error != TABLE_ERROR_NOT_FOUND) {
+        if (error_p != NULL)
+            *error_p = error;
+        return NULL;
+    }
+
+    if (compare == NULL) {
+        /* this is regardless of the alignment */
+        comp_func = local_compare;
+    }
+    else if (table_p->ta_data_align == 0)
+        comp_func = external_compare;
+    else
+        comp_func = external_compare_align;
+    /* now qsort the entire entries array from first to last element */
+    split(entries, entries + table_p->ta_entry_n - 1, comp_func, compare,
+          table_p);
+
+    if (num_entries_p != NULL)
+        *num_entries_p = table_p->ta_entry_n;
+    if (error_p != NULL)
+        *error_p = TABLE_ERROR_NONE;
+    return entries;
+}
+
+/*
+ * int table_entry
+ *
+ * DESCRIPTION:
+ *
+ * Get information about an element.  The element is one from the
+ * array returned by the table_order function.  If any of the key/data
+ * pointers are NULL then they are ignored.
+ *
+ * RETURNS:
+ *
+ * Success - TABLE_ERROR_NONE
+ *
+ * Failure - Table error code.
+ *
+ * ARGUMENTS:
+ *
+ * table_p - Table structure pointer from which we are getting the
+ * element.
+ *
+ * entry_p - Pointer to a table entry from the array returned by the
+ * table_order function.
+ *
+ * key_buf_p - Pointer which, if not NULL, will be set to the address
+ * of the storage of this entry that is allocated in the table.  If an
+ * (int) is stored as this entry (for example) then key_buf_p should
+ * be (int **) i.e. the address of a (int *).
+ *
+ * key_size_p - Pointer to an integer which, if not NULL, will be set
+ * to the size of the key that is stored in the table.
+ *
+ * data_buf_p - Pointer which, if not NULL, will be set to the address
+ * of the data storage of this entry that is allocated in the table.
+ * If a (long) is stored as this entry data (for example) then
+ * data_buf_p should be (long **) i.e. the address of a (long *).
+ *
+ * data_size_p - Pointer to an integer which, if not NULL, will be set
+ * to the size of the data that is stored in the table.
+ */
+int table_entry_info(table_t * table_p, table_entry_t * entry_p,
+                void **key_buf_p, int *key_size_p,
+                void **data_buf_p, int *data_size_p)
+{
+    if (table_p == NULL)
+        return TABLE_ERROR_ARG_NULL;
+    if (table_p->ta_magic != TABLE_MAGIC)
+        return TABLE_ERROR_PNT;
+    if (entry_p == NULL)
+        return TABLE_ERROR_ARG_NULL;
+    if (key_buf_p != NULL)
+        *key_buf_p = ENTRY_KEY_BUF(entry_p);
+    if (key_size_p != NULL)
+        *key_size_p = entry_p->te_key_size;
+    if (data_buf_p != NULL) {
+        if (entry_p->te_data_size == 0)
+            *data_buf_p = NULL;
+        else {
+            if (table_p->ta_data_align == 0)
+                *data_buf_p = ENTRY_DATA_BUF(table_p, entry_p);
+            else
+                *data_buf_p = entry_data_buf(table_p, entry_p);
+        }
+    }
+    if (data_size_p != NULL)
+        *data_size_p = entry_p->te_data_size;
+    return TABLE_ERROR_NONE;
+}
diff --git a/modules/ssl/ssl_util_table.h b/modules/ssl/ssl_util_table.h
new file mode 100644 (file)
index 0000000..69c53bd
--- /dev/null
@@ -0,0 +1,189 @@
+/*                      _             _
+**  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
+** | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
+** | | | | | | (_) | (_| |   \__ \__ \ |  www.modssl.org
+** |_| |_| |_|\___/ \__,_|___|___/___/_|  ftp.modssl.org
+**                      |_____|
+**  ssl_util_table.h
+**  High Performance Hash Table Header
+*/
+
+/* ====================================================================
+ * Copyright (c) 1999-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ *    nor may "mod_ssl" appear in their names without prior
+ *    written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by
+ *     Ralf S. Engelschall <rse@engelschall.com> for use in the
+ *     mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+/*
+ * Generic hash table defines
+ * Table 4.1.0 July-28-1998
+ *
+ * This library is a generic open hash table with buckets and
+ * linked lists.  It is pretty high performance.  Each element
+ * has a key and a data.  The user indexes on the key to find the
+ * data.
+ *
+ * Copyright 1998 by Gray Watson <gray@letters.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose and without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies,
+ * and that the name of Gray Watson not be used in advertising or
+ * publicity pertaining to distribution of the document or software
+ * without specific, written prior permission.
+ *
+ * Gray Watson makes no representations about the suitability of the
+ * software described herein for any purpose.  It is provided "as is"
+ * without express or implied warranty.
+ */
+
+#ifndef SSL_UTIL_TABLE_H
+#define SSL_UTIL_TABLE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*
+ * To build a "key" in any of the below routines, pass in a pointer to
+ * the key and its size [i.e. sizeof(int), etc].  With any of the
+ * "key" or "data" arguments, if their size is < 0, it will do an
+ * internal strlen of the item and add 1 for the \0.
+ *
+ * If you are using firstkey() and nextkey() functions, be careful if,
+ * after starting your firstkey loop, you use delete or insert, it
+ * will not crash but may produce interesting results.  If you are
+ * deleting from firstkey to NULL it will work fine.
+ */
+
+/* return types for table functions */
+#define TABLE_ERROR_NONE    1   /* no error from function */
+#define TABLE_ERROR_PNT     2   /* bad table pointer */
+#define TABLE_ERROR_ARG_NULL    3   /* buffer args were null */
+#define TABLE_ERROR_SIZE    4   /* size of data was bad */
+#define TABLE_ERROR_OVERWRITE   5   /* key exists and we cant overwrite */
+#define TABLE_ERROR_NOT_FOUND   6   /* key does not exist */
+#define TABLE_ERROR_ALLOC   7   /* memory allocation error */
+#define TABLE_ERROR_LINEAR  8   /* no linear access started */
+#define TABLE_ERROR_OPEN    9   /* could not open file */
+#define TABLE_ERROR_SEEK    10  /* could not seek to pos in file */
+#define TABLE_ERROR_READ    11  /* could not read from file */
+#define TABLE_ERROR_WRITE   12  /* could not write to file */
+#define TABLE_ERROR_EMPTY   13  /* table is empty */
+#define TABLE_ERROR_NOT_EMPTY   14  /* table contains data */
+#define TABLE_ERROR_ALIGNMENT   15  /* invalid alignment value */
+
+/*
+ * Table flags set with table_attr.
+ */
+
+/*
+ * Automatically adjust the number of table buckets on the fly.
+ * Whenever the number of entries gets above some threshold, the
+ * number of buckets is realloced to a new size and each entry is
+ * re-hashed.  Although this may take some time when it re-hashes, the
+ * table will perform better over time.
+ */
+#define TABLE_FLAG_AUTO_ADJUST  (1<<0)
+
+/*
+ * If the above auto-adjust flag is set, also adjust the number of
+ * table buckets down as we delete entries.
+ */
+#define TABLE_FLAG_ADJUST_DOWN  (1<<1)
+
+/* structure to walk through the fields in a linear order */
+typedef struct {
+  unsigned int  tl_magic;   /* magic structure to ensure correct init */
+  unsigned int  tl_bucket_c;    /* where in the table buck array we are */
+  unsigned int  tl_entry_c; /* in the bucket, which entry we are on */
+} table_linear_t;
+
+typedef int (*table_compare_t)(const void *key1, const int key1_size,
+                   const void *data1, const int data1_size,
+                   const void *key2, const int key2_size,
+                   const void *data2, const int data2_size);
+
+#ifndef TABLE_PRIVATE
+typedef void    table_t;
+typedef void    table_entry_t;
+#endif
+
+/*
+ * Prototypes
+ */
+extern table_t        *table_alloc(const unsigned int bucket_n, int *error_p, void *(*malloc_f)(size_t size), void *(*calloc_f)(size_t number, size_t size), void *(*realloc_f)(void *ptr, size_t size), void (*free_f)(void *ptr));
+extern int             table_attr(table_t *table_p, const int attr);
+extern int             table_set_data_alignment(table_t *table_p, const int alignment);
+extern int             table_clear(table_t *table_p);
+extern int             table_free(table_t *table_p);
+extern int             table_insert_kd(table_t *table_p, const void *key_buf, const int key_size, const void *data_buf, const int data_size, void **key_buf_p, void **data_buf_p, const char overwrite_b);
+extern int             table_insert(table_t *table_p, const void *key_buf, const int key_size, const void *data_buf, const int data_size, void **data_buf_p, const char overwrite_b);
+extern int             table_retrieve(table_t *table_p, const void *key_buf, const int key_size, void **data_buf_p, int *data_size_p);
+extern int             table_delete(table_t *table_p, const void *key_buf, const int key_size, void **data_buf_p, int *data_size_p);
+extern int             table_delete_first(table_t *table_p, void **key_buf_p, int *key_size_p, void **data_buf_p, int *data_size_p);
+extern int             table_info(table_t *table_p, int *num_buckets_p, int *num_entries_p);
+extern int             table_adjust(table_t *table_p, const int bucket_n);
+extern const char     *table_strerror(const int error);
+extern int             table_type_size(void);
+extern int             table_first(table_t *table_p, void **key_buf_p, int *key_size_p, void **data_buf_p, int *data_size_p);
+extern int             table_next(table_t *table_p, void **key_buf_p, int *key_size_p, void **data_buf_p, int *data_size_p);
+extern int             table_this(table_t *table_p, void **key_buf_p, int *key_size_p, void **data_buf_p, int *data_size_p);
+extern int             table_first_r(table_t *table_p, table_linear_t *linear_p, void **key_buf_p, int *key_size_p, void **data_buf_p, int *data_size_p);
+extern int             table_next_r(table_t *table_p, table_linear_t *linear_p, void **key_buf_p, int *key_size_p, void **data_buf_p, int *data_size_p);
+extern int             table_this_r(table_t *table_p, table_linear_t *linear_p, void **key_buf_p, int *key_size_p, void **data_buf_p, int *data_size_p);
+extern table_t        *table_read(const char *path, int *error_p, void *(*malloc_f)(size_t size), void *(*calloc_f)(size_t number, size_t size), void *(*realloc_f)(void *ptr, size_t size), void (*free_f)(void *ptr));
+extern int             table_write(const table_t *table_p, const char *path, const int mode);
+extern table_entry_t **table_order(table_t *table_p, table_compare_t compare, int *num_entries_p, int *error_p);
+extern int             table_entry_info(table_t *table_p, table_entry_t *entry_p, void **key_buf_p, int *key_size_p, void **data_buf_p, int *data_size_p);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* ! SSL_UTIL_TABLE_H */