mod_vhostdb - vhost docroot lookups backends: mod_vhostdb_dbi mod_vhostdb_ldap mod_vhostdb_mysql (now preferred over mod_mysql_vhost.c) mod_vhostdb_pgsql STATUS: experimental (testing and feedback appreciated) x-ref: "PostgreSQL virtual host support" https://redmine.lighttpd.net/issues/485 "LDAP Virtual Host Definition Storage Integration" https://redmine.lighttpd.net/issues/1936 "mod_dbi_vhost (patch included)" https://redmine.lighttpd.net/issues/2297
This commit is contained in:
parent
eda72ebfc7
commit
2f83aac9fb
17
SConstruct
17
SConstruct
|
@ -115,6 +115,8 @@ vars.AddVariables(
|
|||
('sbindir', 'binary directory', '${prefix}/sbin'),
|
||||
('libdir', 'library directory', '${prefix}/lib'),
|
||||
PackageVariable('with_mysql', 'enable mysql support', 'no'),
|
||||
PackageVariable('with_pgsql', 'enable pgsql support', 'no'),
|
||||
PackageVariable('with_dbi', 'enable dbi support', 'no'),
|
||||
PackageVariable('with_xml', 'enable xml support', 'no'),
|
||||
PackageVariable('with_pcre', 'enable pcre support', 'yes'),
|
||||
PathVariable('CC', 'path to the c-compiler', None),
|
||||
|
@ -227,6 +229,7 @@ if 1:
|
|||
checkTypes(autoconf, Split('pid_t size_t off_t'))
|
||||
|
||||
autoconf.env.Append( LIBSQLITE3 = '', LIBXML2 = '', LIBMYSQL = '', LIBZ = '',
|
||||
LIBPGSQL = '', LIBDBI = '',
|
||||
LIBBZ2 = '', LIBCRYPT = '', LIBMEMCACHED = '', LIBFCGI = '', LIBPCRE = '',
|
||||
LIBLDAP = '', LIBLBER = '', LIBLUA = '', LIBDL = '', LIBUUID = '',
|
||||
LIBKRB5 = '', LIBGSSAPI_KRB5 = '', LIBGDBM = '', LIBSSL = '', LIBCRYPTO = '')
|
||||
|
@ -362,6 +365,20 @@ if env['with_mysql']:
|
|||
env.Append(CPPFLAGS = [ '-DHAVE_MYSQL_H', '-DHAVE_LIBMYSQL' ], LIBMYSQL = 'mysqlclient')
|
||||
env['LIBS'] = oldlib
|
||||
|
||||
if env['with_pgsql']:
|
||||
pg_config = checkProgram(env, 'pgsql', 'pg_config')
|
||||
oldlib = env['LIBS']
|
||||
env['LIBS'] = []
|
||||
env.ParseConfig(pg_config + ' --includedir --libdir')
|
||||
env.Append(CPPFLAGS = [ '-DHAVE_PGSQL_H', '-DHAVE_LIBPGSQL' ], LIBPGSQL = 'pq')
|
||||
env['LIBS'] = oldlib
|
||||
#if autoconf.CheckLibWithHeader('pq', 'libpq-fe.h', 'C'):
|
||||
# env.Append(CPPFLAGS = [ '-DHAVE_PGSQL_H', '-DHAVE_LIBPGSQL' ], LIBPGSQL = 'pq')
|
||||
|
||||
if env['with_dbi']:
|
||||
if autoconf.CheckLibWithHeader('dbi', 'dbi/dbi.h', 'C'):
|
||||
env.Append(CPPFLAGS = [ '-DHAVE_DBI_H', '-DHAVE_LIBDBI' ], LIBDBI = 'dbi')
|
||||
|
||||
if re.compile("cygwin|mingw|midipix").search(env['PLATFORM']):
|
||||
env.Append(COMMON_LIB = 'bin')
|
||||
elif re.compile("darwin|aix").search(env['PLATFORM']):
|
||||
|
|
101
configure.ac
101
configure.ac
|
@ -231,6 +231,88 @@ AM_CONDITIONAL(BUILD_WITH_MYSQL, test ! $WITH_MYSQL = no)
|
|||
AC_SUBST(MYSQL_LIBS)
|
||||
AC_SUBST(MYSQL_INCLUDE)
|
||||
|
||||
|
||||
dnl Checks for database.
|
||||
PGSQL_INCLUDE=""
|
||||
PGSQL_LIBS=""
|
||||
|
||||
AC_MSG_CHECKING(for PgSQL support)
|
||||
AC_ARG_WITH(pgsql,
|
||||
AC_HELP_STRING([--with-pgsql@<:@=PATH@:>@],[Include PgSQL support. PATH is the path to 'pg_config']),
|
||||
[WITH_PGSQL=$withval],[WITH_PGSQL=no])
|
||||
|
||||
if test "$WITH_PGSQL" != "no"; then
|
||||
AC_MSG_RESULT(yes)
|
||||
if test "$WITH_PGSQL" = "yes"; then
|
||||
AC_PATH_PROG(PGSQL_CONFIG, pg_config)
|
||||
else
|
||||
PGSQL_CONFIG=$WITH_PGSQL
|
||||
fi
|
||||
|
||||
if test "$PGSQL_CONFIG" = ""; then
|
||||
AC_MSG_ERROR(pg_config is not found)
|
||||
fi
|
||||
if test \! -x $PGSQL_CONFIG; then
|
||||
AC_MSG_ERROR(pg_config not exists or not executable, use --with-pgsql=path-to-pg_config)
|
||||
fi
|
||||
|
||||
PGSQL_INCLUDE="-I`$PGSQL_CONFIG --includedir`"
|
||||
PGSQL_LIBS="-L`$PGSQL_CONFIG --libdir` -lpq"
|
||||
|
||||
AC_MSG_CHECKING(for PgSQL includes at)
|
||||
AC_MSG_RESULT($PGSQL_INCLUDE)
|
||||
|
||||
AC_MSG_CHECKING(for PgSQL libraries at)
|
||||
AC_MSG_RESULT($PGSQL_LIBS)
|
||||
|
||||
AC_DEFINE([HAVE_PGSQL], [1], [pgsql support])
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
AM_CONDITIONAL(BUILD_WITH_PGSQL, test ! $WITH_PGSQL = no)
|
||||
|
||||
AC_SUBST(PGSQL_LIBS)
|
||||
AC_SUBST(PGSQL_INCLUDE)
|
||||
|
||||
|
||||
dnl Checks for libdbi library
|
||||
DBI_INCLUDE=""
|
||||
DBI_LIBS=""
|
||||
|
||||
AC_MSG_CHECKING(for LibDBI support)
|
||||
AC_ARG_WITH(dbi,
|
||||
AC_HELP_STRING([--with-dbi@<:@=PATH@:>@],[Include DBI support in PATH/include/dbi.h and PATH/lib]),
|
||||
[WITH_DBI=$withval],[WITH_DBI=no])
|
||||
|
||||
if test "$WITH_DBI" != "no"; then
|
||||
AC_MSG_RESULT(yes)
|
||||
if test "$WITH_DBI" != "yes"; then
|
||||
DBI_CFLAGS="-I$WITH_LIBDBI/include"
|
||||
DBI_LIBS="-L$WITH_LIBDBI/lib -ldbi"
|
||||
else
|
||||
AC_CHECK_HEADERS([dbi/dbi.h],[
|
||||
AC_CHECK_LIB([dbi], [dbi_version], [
|
||||
DBI_CFLAGS=""
|
||||
DBI_LIBS="-ldbi"
|
||||
],[
|
||||
AC_MSG_ERROR([LibDBI not found])
|
||||
]
|
||||
)],[
|
||||
AC_MSG_ERROR([LibDBI not found])
|
||||
]
|
||||
)
|
||||
fi
|
||||
|
||||
AC_DEFINE([HAVE_DBI], [1], [LibDBI support])
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
AM_CONDITIONAL(BUILD_WITH_DBI, test ! $WITH_DBI = no)
|
||||
|
||||
AC_SUBST(DBI_LIBS)
|
||||
AC_SUBST(DBI_CFLAGS)
|
||||
|
||||
|
||||
dnl Check for LDAP
|
||||
AC_MSG_CHECKING(for LDAP support)
|
||||
AC_ARG_WITH(ldap, AC_HELP_STRING([--with-ldap],[enable LDAP support]),
|
||||
|
@ -921,6 +1003,7 @@ AC_OUTPUT
|
|||
|
||||
do_build="mod_cgi mod_fastcgi mod_extforward mod_proxy mod_evhost mod_simple_vhost mod_access mod_alias mod_setenv mod_usertrack mod_auth mod_authn_file mod_status mod_accesslog"
|
||||
do_build="$do_build mod_rrdtool mod_secdownload mod_expire mod_compress mod_dirlisting mod_indexfile mod_userdir mod_webdav mod_staticfile mod_scgi mod_flv_streaming mod_ssi mod_deflate"
|
||||
do_build="$do_build mod_vhostdb"
|
||||
|
||||
plugins="mod_rewrite mod_redirect"
|
||||
features="regex-conditionals"
|
||||
|
@ -941,13 +1024,27 @@ else
|
|||
fi
|
||||
fi
|
||||
|
||||
plugins="mod_authn_mysql mod_mysql_vhost"
|
||||
plugins="mod_authn_mysql mod_mysql_vhost mod_vhostdb_mysql"
|
||||
if test ! "x$MYSQL_LIBS" = x; then
|
||||
do_build="$do_build $plugins"
|
||||
else
|
||||
no_build="$no_build $plugins"
|
||||
fi
|
||||
|
||||
plugins="mod_vhostdb_pgsql"
|
||||
if test ! "x$PGSQL_LIBS" = x; then
|
||||
do_build="$do_build $plugins"
|
||||
else
|
||||
no_build="$no_build $plugins"
|
||||
fi
|
||||
|
||||
plugins="mod_vhostdb_dbi"
|
||||
if test ! "x$DBI_LIBS" = x; then
|
||||
do_build="$do_build $plugins"
|
||||
else
|
||||
no_build="$no_build $plugins"
|
||||
fi
|
||||
|
||||
plugins="mod_cml mod_magnet"
|
||||
if test ! "x$LUA_LIBS" = x; then
|
||||
do_build="$do_build $plugins"
|
||||
|
@ -997,7 +1094,7 @@ else
|
|||
no_build="$no_build $plugins"
|
||||
fi
|
||||
|
||||
plugins="mod_authn_ldap"
|
||||
plugins="mod_authn_ldap mod_vhostdb_ldap"
|
||||
if test ! "x$LDAP_LIB" = x; then
|
||||
do_build="$do_build $plugins"
|
||||
else
|
||||
|
|
|
@ -14,19 +14,19 @@ include(LighttpdMacros)
|
|||
add_definitions(-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGE_FILES)
|
||||
|
||||
option(WITH_XATTR "with xattr-support for the stat-cache [default: off]")
|
||||
option(WITH_MYSQL "with mysql-support for the mod_sql_vhost [default: off]")
|
||||
# option(WITH_POSTGRESQL "with postgress-support for the mod_sql_vhost [default: off]")
|
||||
option(WITH_MYSQL "with mysql-support for mod_vhostdb_mysql [default: off]")
|
||||
option(WITH_PGSQL "with postgres-support for mod_vhostdb_pgsql [default: off]")
|
||||
option(WITH_DBI "with dbi-support for mod_vhostdb_dbi [default: off]")
|
||||
option(WITH_OPENSSL "with openssl-support [default: off]")
|
||||
option(WITH_PCRE "with regex support [default: on]" ON)
|
||||
option(WITH_WEBDAV_PROPS "with property-support for mod_webdav [default: off]")
|
||||
option(WITH_WEBDAV_LOCKS "locks in webdav [default: off]")
|
||||
option(WITH_BZIP "with bzip2-support for mod_compress [default: off]")
|
||||
option(WITH_ZLIB "with deflate-support for mod_compress [default: on]" ON)
|
||||
option(WITH_KRB5 "with Kerberos5-support for the mod_auth [default: off]")
|
||||
option(WITH_LDAP "with LDAP-support for the mod_auth [default: off]")
|
||||
option(WITH_KRB5 "with Kerberos5-support for mod_auth [default: off]")
|
||||
option(WITH_LDAP "with LDAP-support for mod_auth mod_vhostdb_ldap [default: off]")
|
||||
option(WITH_LUA "with lua 5.1 for mod_magnet [default: off]")
|
||||
# option(WITH_VALGRIND "with internal support for valgrind [default: off]")
|
||||
# option(WITH_KERBEROS5 "use Kerberos5 support with OpenSSL [default: off]")
|
||||
option(WITH_FAM "fam/gamin for reducing number of stat() calls [default: off]")
|
||||
option(WITH_GDBM "gdbm storage for mod_trigger_b4_dl [default: off]")
|
||||
option(WITH_MEMCACHED "memcached storage for mod_trigger_b4_dl [default: off]")
|
||||
|
@ -248,6 +248,28 @@ else()
|
|||
unset(HAVE_MYSQL)
|
||||
endif()
|
||||
|
||||
if(WITH_PGSQL)
|
||||
xconfig(pg_config PGSQL_INCDIR PGSQL_LIBDIR PGSQL_LDFLAGS PGSQL_CFLAGS)
|
||||
|
||||
check_include_files(libpq-fe.h HAVE_PGSQL_H)
|
||||
if(HAVE_PGSQL_H)
|
||||
check_library_exists(pq PQsetdbLogin "" HAVE_PGSQL)
|
||||
endif()
|
||||
else()
|
||||
unset(HAVE_PGSQL_H)
|
||||
unset(HAVE_PGSQL)
|
||||
endif()
|
||||
|
||||
if(WITH_DBI)
|
||||
check_include_files(dbi/dbi.h HAVE_DBI_H)
|
||||
if(HAVE_DBI_H)
|
||||
check_library_exists(dbi dbi_conn_connect "" HAVE_DBI)
|
||||
endif()
|
||||
else()
|
||||
unset(HAVE_DBI_H)
|
||||
unset(HAVE_DBI)
|
||||
endif()
|
||||
|
||||
if(WITH_OPENSSL)
|
||||
if(APPLE)
|
||||
set(CMAKE_REQUIRED_INCLUDES /opt/local/include)
|
||||
|
@ -530,6 +552,7 @@ set(COMMON_SRC
|
|||
configfile-glue.c
|
||||
http-header-glue.c
|
||||
http_auth.c
|
||||
http_vhostdb.c
|
||||
splaytree.c
|
||||
rand.c
|
||||
status_counter.c safe_memclear.c
|
||||
|
@ -605,6 +628,7 @@ add_and_install_library(mod_status mod_status.c)
|
|||
add_and_install_library(mod_uploadprogress mod_uploadprogress.c)
|
||||
add_and_install_library(mod_userdir mod_userdir.c)
|
||||
add_and_install_library(mod_usertrack mod_usertrack.c)
|
||||
add_and_install_library(mod_vhostdb mod_vhostdb.c)
|
||||
add_and_install_library(mod_webdav mod_webdav.c)
|
||||
|
||||
add_executable(test_buffer
|
||||
|
@ -668,6 +692,8 @@ endif()
|
|||
if(HAVE_MYSQL_H AND HAVE_MYSQL)
|
||||
add_and_install_library(mod_mysql_vhost "mod_mysql_vhost.c")
|
||||
target_link_libraries(mod_mysql_vhost mysqlclient)
|
||||
add_and_install_library(mod_vhostdb_mysql "mod_vhostdb_mysql.c")
|
||||
target_link_libraries(mod_vhostdb_mysql mysqlclient)
|
||||
include_directories(/usr/include/mysql)
|
||||
|
||||
add_and_install_library(mod_authn_mysql "mod_authn_mysql.c")
|
||||
|
@ -678,6 +704,16 @@ if(HAVE_MYSQL_H AND HAVE_MYSQL)
|
|||
target_link_libraries(mod_authn_mysql ${L_MOD_AUTHN_MYSQL} mysqlclient)
|
||||
endif()
|
||||
|
||||
if(HAVE_PGSQL_H AND HAVE_PGSQL)
|
||||
add_and_install_library(mod_vhostdb_pgsql "mod_vhostdb_pgsql.c")
|
||||
target_link_libraries(mod_vhostdb_pgsql pq)
|
||||
endif()
|
||||
|
||||
if(HAVE_DBI_H AND HAVE_DBI)
|
||||
add_and_install_library(mod_vhostdb_dbi "mod_vhostdb_dbi.c")
|
||||
target_link_libraries(mod_vhostdb_dbi dbi)
|
||||
endif()
|
||||
|
||||
set(L_MOD_WEBDAV)
|
||||
if(HAVE_SQLITE3_H)
|
||||
set(L_MOD_WEBDAV ${L_MOD_WEBDAV} sqlite3)
|
||||
|
@ -707,9 +743,11 @@ if(WITH_KRB5)
|
|||
endif()
|
||||
|
||||
if(WITH_LDAP)
|
||||
add_and_install_library(mod_authn_ldap "mod_authn_ldap.c")
|
||||
set(L_MOD_AUTHN_LDAP ${L_MOD_AUTHN_LDAP} ldap lber)
|
||||
add_and_install_library(mod_authn_ldap "mod_authn_ldap.c")
|
||||
target_link_libraries(mod_authn_ldap ${L_MOD_AUTHN_LDAP})
|
||||
add_and_install_library(mod_vhostdb_ldap "mod_vhostdb_ldap.c")
|
||||
target_link_libraries(mod_vhostdb_ldap ${L_MOD_AUTHN_LDAP})
|
||||
endif()
|
||||
|
||||
if(HAVE_ZLIB_H)
|
||||
|
|
|
@ -72,6 +72,7 @@ common_src=base64.c buffer.c log.c \
|
|||
configfile-glue.c \
|
||||
http-header-glue.c \
|
||||
http_auth.c \
|
||||
http_vhostdb.c \
|
||||
rand.c \
|
||||
splaytree.c status_counter.c \
|
||||
safe_memclear.c
|
||||
|
@ -150,6 +151,18 @@ mod_trigger_b4_dl_la_LDFLAGS = $(common_module_ldflags)
|
|||
mod_trigger_b4_dl_la_LIBADD = $(GDBM_LIB) $(MEMCACHED_LIB) $(PCRE_LIB) $(common_libadd)
|
||||
endif
|
||||
|
||||
lib_LTLIBRARIES += mod_vhostdb.la
|
||||
mod_vhostdb_la_SOURCES = mod_vhostdb.c
|
||||
mod_vhostdb_la_LDFLAGS = $(common_module_ldflags)
|
||||
mod_vhostdb_la_LIBADD = $(common_libadd)
|
||||
|
||||
if BUILD_WITH_LDAP
|
||||
lib_LTLIBRARIES += mod_vhostdb_ldap.la
|
||||
mod_vhostdb_ldap_la_SOURCES = mod_vhostdb_ldap.c
|
||||
mod_vhostdb_ldap_la_LDFLAGS = $(common_module_ldflags)
|
||||
mod_vhostdb_ldap_la_LIBADD = $(LDAP_LIB) $(LBER_LIB) $(common_libadd)
|
||||
endif
|
||||
|
||||
if BUILD_WITH_MYSQL
|
||||
lib_LTLIBRARIES += mod_mysql_vhost.la
|
||||
mod_mysql_vhost_la_SOURCES = mod_mysql_vhost.c
|
||||
|
@ -158,6 +171,30 @@ mod_mysql_vhost_la_LIBADD = $(MYSQL_LIBS) $(common_libadd)
|
|||
mod_mysql_vhost_la_CPPFLAGS = $(MYSQL_INCLUDE)
|
||||
endif
|
||||
|
||||
if BUILD_WITH_MYSQL
|
||||
lib_LTLIBRARIES += mod_vhostdb_mysql.la
|
||||
mod_vhostdb_mysql_la_SOURCES = mod_vhostdb_mysql.c
|
||||
mod_vhostdb_mysql_la_LDFLAGS = $(common_module_ldflags)
|
||||
mod_vhostdb_mysql_la_LIBADD = $(MYSQL_LIBS) $(common_libadd)
|
||||
mod_vhostdb_mysql_la_CPPFLAGS = $(MYSQL_INCLUDE)
|
||||
endif
|
||||
|
||||
if BUILD_WITH_PGSQL
|
||||
lib_LTLIBRARIES += mod_vhostdb_pgsql.la
|
||||
mod_vhostdb_pgsql_la_SOURCES = mod_vhostdb_pgsql.c
|
||||
mod_vhostdb_pgsql_la_LDFLAGS = $(common_module_ldflags)
|
||||
mod_vhostdb_pgsql_la_LIBADD = $(PGSQL_LIBS) $(common_libadd)
|
||||
mod_vhostdb_pgsql_la_CPPFLAGS = $(PGSQL_INCLUDE)
|
||||
endif
|
||||
|
||||
if BUILD_WITH_DBI
|
||||
lib_LTLIBRARIES += mod_vhostdb_dbi.la
|
||||
mod_vhostdb_dbi_la_SOURCES = mod_vhostdb_dbi.c
|
||||
mod_vhostdb_dbi_la_LDFLAGS = $(common_module_ldflags)
|
||||
mod_vhostdb_dbi_la_LIBADD = $(DBI_LIBS) $(common_libadd)
|
||||
mod_vhostdb_dbi_la_CPPFLAGS = $(DBI_CFLAGS)
|
||||
endif
|
||||
|
||||
lib_LTLIBRARIES += mod_cgi.la
|
||||
mod_cgi_la_SOURCES = mod_cgi.c
|
||||
mod_cgi_la_LDFLAGS = $(common_module_ldflags)
|
||||
|
@ -336,7 +373,7 @@ mod_uploadprogress_la_LIBADD = $(common_libadd)
|
|||
hdr = server.h base64.h buffer.h network.h log.h keyvalue.h \
|
||||
response.h request.h fastcgi.h chunk.h \
|
||||
first.h settings.h http_chunk.h \
|
||||
md5.h http_auth.h stream.h \
|
||||
md5.h http_auth.h http_vhostdb.h stream.h \
|
||||
fdevent.h connections.h base.h stat_cache.h \
|
||||
plugin.h \
|
||||
etag.h joblist.h array.h vector.h crc32.h \
|
||||
|
@ -387,6 +424,7 @@ lighttpd_SOURCES = \
|
|||
mod_uploadprogress.c \
|
||||
mod_userdir.c \
|
||||
mod_usertrack.c \
|
||||
mod_vhostdb.c \
|
||||
mod_webdav.c
|
||||
lighttpd_CPPFLAGS = \
|
||||
-DLIGHTTPD_STATIC \
|
||||
|
@ -415,14 +453,24 @@ lighttpd_SOURCES += mod_authn_gssapi.c
|
|||
lighttpd_LDADD += $(KRB5_LIB)
|
||||
endif
|
||||
if BUILD_WITH_LDAP
|
||||
lighttpd_SOURCES += mod_authn_ldap.c
|
||||
lighttpd_SOURCES += mod_authn_ldap.c mod_vhostdb_ldap.c
|
||||
lighttpd_LDADD += $(LDAP_LIB) $(LBER_LIB)
|
||||
endif
|
||||
if BUILD_WITH_MYSQL
|
||||
lighttpd_SOURCES += mod_authn_mysql.c mod_mysql_vhost.c
|
||||
lighttpd_SOURCES += mod_authn_mysql.c mod_mysql_vhost.c mod_vhostdb_mysql.c
|
||||
lighttpd_CPPFLAGS += $(MYSQL_INCLUDE)
|
||||
lighttpd_LDADD += $(MYSQL_LIBS)
|
||||
endif
|
||||
if BUILD_WITH_PGSQL
|
||||
lighttpd_SOURCES += mod_vhostdb_pgsql.c
|
||||
lighttpd_CPPFLAGS += $(PGSQL_INCLUDE)
|
||||
lighttpd_LDADD += $(PGSQL_LIBS)
|
||||
endif
|
||||
if BUILD_WITH_DBI
|
||||
lighttpd_SOURCES += mod_vhostdb_dbi.c
|
||||
lighttpd_CPPFLAGS += $(DBI_CFLAGS)
|
||||
lighttpd_LDADD += $(DBI_LIBS)
|
||||
endif
|
||||
if BUILD_WITH_OPENSSL
|
||||
lighttpd_SOURCES += mod_openssl.c
|
||||
lighttpd_LDADD += $(SSL_LIB)
|
||||
|
|
|
@ -66,6 +66,7 @@ common_src = Split("base64.c buffer.c log.c \
|
|||
configfile-glue.c \
|
||||
http-header-glue.c \
|
||||
http_auth.c \
|
||||
http_vhostdb.c \
|
||||
splaytree.c \
|
||||
rand.c \
|
||||
status_counter.c safe_memclear.c \
|
||||
|
@ -122,6 +123,7 @@ modules = {
|
|||
'mod_evasive' : { 'src' : [ 'mod_evasive.c' ] },
|
||||
'mod_ssi' : { 'src' : [ 'mod_ssi_exprparser.c', 'mod_ssi_expr.c', 'mod_ssi.c' ] },
|
||||
'mod_flv_streaming' : { 'src' : [ 'mod_flv_streaming.c' ] },
|
||||
'mod_vhostdb' : { 'src' : [ 'mod_vhostdb.c' ] },
|
||||
}
|
||||
|
||||
if env['with_geoip']:
|
||||
|
@ -132,6 +134,7 @@ if env['with_krb5']:
|
|||
|
||||
if env['with_ldap']:
|
||||
modules['mod_authn_ldap'] = { 'src' : [ 'mod_authn_ldap.c' ], 'lib' : [ env['LIBLDAP'], env['LIBLBER'] ] }
|
||||
modules['mod_vhostdb_ldap'] = { 'src' : [ 'mod_vhostdb_ldap.c' ], 'lib' : [ env['LIBLDAP'], env['LIBLBER'] ] }
|
||||
|
||||
if env['with_lua']:
|
||||
modules['mod_magnet'] = { 'src' : [ 'mod_magnet.c', 'mod_magnet_cache.c' ], 'lib' : [ env['LIBLUA'] ] }
|
||||
|
@ -146,6 +149,13 @@ if env['with_pcre'] and (env['with_memcached'] or env['with_gdbm']):
|
|||
if env['with_mysql']:
|
||||
modules['mod_authn_mysql'] = { 'src' : [ 'mod_authn_mysql.c' ], 'lib' : [ env['LIBCRYPT'], env['LIBMYSQL'] ] }
|
||||
modules['mod_mysql_vhost'] = { 'src' : [ 'mod_mysql_vhost.c' ], 'lib' : [ env['LIBMYSQL'] ] }
|
||||
modules['mod_vhostdb_mysql'] = { 'src' : [ 'mod_vhostdb_mysql.c' ], 'lib' : [ env['LIBMYSQL'] ] }
|
||||
|
||||
if env['with_pgsql']:
|
||||
modules['mod_vhostdb_pgsql'] = { 'src' : [ 'mod_vhostdb_pgsql.c' ], 'lib' : [ env['LIBPGSQL'] ] }
|
||||
|
||||
if env['with_dbi']:
|
||||
modules['mod_vhostdb_dbi'] = { 'src' : [ 'mod_vhostdb_dbi.c' ], 'lib' : [ env['LIBDBI'] ] }
|
||||
|
||||
if env['with_openssl']:
|
||||
modules['mod_openssl'] = { 'src' : [ 'mod_openssl.c' ], 'lib' : [ env['LIBSSL'], env['LIBCRYPTO'] ] }
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
#include "first.h"
|
||||
|
||||
#include "http_vhostdb.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
||||
static http_vhostdb_backend_t http_vhostdb_backends[8];
|
||||
|
||||
const http_vhostdb_backend_t * http_vhostdb_backend_get (const buffer *name)
|
||||
{
|
||||
int i = 0;
|
||||
while (NULL != http_vhostdb_backends[i].name
|
||||
&& 0 != strcmp(http_vhostdb_backends[i].name, name->ptr)) {
|
||||
++i;
|
||||
}
|
||||
return (NULL != http_vhostdb_backends[i].name)
|
||||
? http_vhostdb_backends+i
|
||||
: NULL;
|
||||
}
|
||||
|
||||
void http_vhostdb_backend_set (const http_vhostdb_backend_t *backend)
|
||||
{
|
||||
unsigned int i = 0;
|
||||
while (NULL != http_vhostdb_backends[i].name) ++i;
|
||||
/*(must resize http_vhostdb_backends[] if too many different backends)*/
|
||||
force_assert(
|
||||
i < (sizeof(http_vhostdb_backends)/sizeof(http_vhostdb_backend_t))-1);
|
||||
memcpy(http_vhostdb_backends+i, backend, sizeof(http_vhostdb_backend_t));
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
#ifndef _HTTP_VHOST_H_
|
||||
#define _HTTP_VHOST_H_
|
||||
#include "first.h"
|
||||
|
||||
#include "base.h"
|
||||
|
||||
struct http_vhostdb_backend_t;
|
||||
|
||||
typedef struct http_vhostdb_backend_t {
|
||||
const char *name;
|
||||
int(*query)(server *srv, connection *con, void *p_d, buffer *result);
|
||||
void *p_d;
|
||||
} http_vhostdb_backend_t;
|
||||
|
||||
const http_vhostdb_backend_t * http_vhostdb_backend_get (const buffer *name);
|
||||
void http_vhostdb_backend_set (const http_vhostdb_backend_t *backend);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,238 @@
|
|||
#include "first.h"
|
||||
|
||||
#include "plugin.h"
|
||||
#include "http_vhostdb.h"
|
||||
#include "log.h"
|
||||
#include "stat_cache.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* vhostdb framework
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
buffer *vhostdb_backend_conf;
|
||||
|
||||
/* generated */
|
||||
const http_vhostdb_backend_t *vhostdb_backend;
|
||||
} plugin_config;
|
||||
|
||||
typedef struct {
|
||||
PLUGIN_DATA;
|
||||
plugin_config **config_storage;
|
||||
plugin_config conf;
|
||||
|
||||
buffer *tmp_buf;
|
||||
} plugin_data;
|
||||
|
||||
INIT_FUNC(mod_vhostdb_init) {
|
||||
plugin_data *p = calloc(1, sizeof(*p));
|
||||
p->tmp_buf = buffer_init();
|
||||
return p;
|
||||
}
|
||||
|
||||
FREE_FUNC(mod_vhostdb_free) {
|
||||
plugin_data *p = p_d;
|
||||
if (!p) return HANDLER_GO_ON;
|
||||
|
||||
if (p->config_storage) {
|
||||
size_t i;
|
||||
for (i = 0; i < srv->config_context->used; i++) {
|
||||
plugin_config *s = p->config_storage[i];
|
||||
if (NULL == s) continue;
|
||||
buffer_free(s->vhostdb_backend_conf);
|
||||
free(s);
|
||||
}
|
||||
free(p->config_storage);
|
||||
}
|
||||
|
||||
free(p->tmp_buf);
|
||||
free(p);
|
||||
|
||||
UNUSED(srv);
|
||||
return HANDLER_GO_ON;
|
||||
}
|
||||
|
||||
SETDEFAULTS_FUNC(mod_vhostdb_set_defaults) {
|
||||
plugin_data *p = p_d;
|
||||
config_values_t cv[] = {
|
||||
{ "vhostdb.backend", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */
|
||||
|
||||
{ NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
|
||||
};
|
||||
|
||||
p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *));
|
||||
|
||||
for (size_t i = 0; i < srv->config_context->used; ++i) {
|
||||
data_config const *config = (data_config const*)srv->config_context->data[i];
|
||||
plugin_config *s = calloc(1, sizeof(plugin_config));
|
||||
s->vhostdb_backend_conf = buffer_init();
|
||||
|
||||
cv[0].destination = s->vhostdb_backend_conf;
|
||||
|
||||
p->config_storage[i] = s;
|
||||
|
||||
if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) {
|
||||
return HANDLER_ERROR;
|
||||
}
|
||||
|
||||
if (!buffer_string_is_empty(s->vhostdb_backend_conf)) {
|
||||
s->vhostdb_backend =
|
||||
http_vhostdb_backend_get(s->vhostdb_backend_conf);
|
||||
if (NULL == s->vhostdb_backend) {
|
||||
log_error_write(srv, __FILE__, __LINE__, "sb",
|
||||
"vhostdb.backend not supported:",
|
||||
s->vhostdb_backend_conf);
|
||||
return HANDLER_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return HANDLER_GO_ON;
|
||||
}
|
||||
|
||||
#define PATCH(x) \
|
||||
p->conf.x = s->x;
|
||||
static int mod_vhostdb_patch_connection(server *srv, connection *con, plugin_data *p) {
|
||||
plugin_config *s = p->config_storage[0];
|
||||
PATCH(vhostdb_backend);
|
||||
|
||||
/* skip the first, the global context */
|
||||
for (size_t i = 1; i < srv->config_context->used; ++i) {
|
||||
data_config *dc = (data_config *)srv->config_context->data[i];
|
||||
s = p->config_storage[i];
|
||||
|
||||
/* condition didn't match */
|
||||
if (!config_check_cond(srv, con, dc)) continue;
|
||||
|
||||
/* merge config */
|
||||
for (size_t j = 0; j < dc->value->used; ++j) {
|
||||
data_unset *du = dc->value->data[j];
|
||||
|
||||
if (buffer_is_equal_string(du->key, CONST_STR_LEN("vhostdb.backend"))) {
|
||||
PATCH(vhostdb_backend);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#undef PATCH
|
||||
|
||||
typedef struct {
|
||||
buffer *server_name;
|
||||
buffer *document_root;
|
||||
} vhostdb_entry;
|
||||
|
||||
static vhostdb_entry * vhostdb_entry_init (void)
|
||||
{
|
||||
vhostdb_entry *ve = calloc(1, sizeof(*ve));
|
||||
ve->server_name = buffer_init();
|
||||
ve->document_root = buffer_init();
|
||||
return ve;
|
||||
}
|
||||
|
||||
static void vhostdb_entry_free (vhostdb_entry *ve)
|
||||
{
|
||||
buffer_free(ve->server_name);
|
||||
buffer_free(ve->document_root);
|
||||
free(ve);
|
||||
}
|
||||
|
||||
CONNECTION_FUNC(mod_vhostdb_handle_connection_close) {
|
||||
plugin_data *p = p_d;
|
||||
vhostdb_entry *ve;
|
||||
|
||||
if ((ve = con->plugin_ctx[p->id])) {
|
||||
con->plugin_ctx[p->id] = NULL;
|
||||
vhostdb_entry_free(ve);
|
||||
}
|
||||
|
||||
UNUSED(srv);
|
||||
return HANDLER_GO_ON;
|
||||
}
|
||||
|
||||
static handler_t mod_vhostdb_error_500 (connection *con)
|
||||
{
|
||||
con->http_status = 500; /* Internal Server Error */
|
||||
con->mode = DIRECT;
|
||||
return HANDLER_FINISHED;
|
||||
}
|
||||
|
||||
static handler_t mod_vhostdb_found (connection *con, vhostdb_entry *ve)
|
||||
{
|
||||
/* fix virtual server and docroot */
|
||||
buffer_copy_buffer(con->server_name, ve->server_name);
|
||||
buffer_copy_buffer(con->physical.doc_root, ve->document_root);
|
||||
return HANDLER_GO_ON;
|
||||
}
|
||||
|
||||
CONNECTION_FUNC(mod_vhostdb_handle_docroot) {
|
||||
plugin_data *p = p_d;
|
||||
vhostdb_entry *ve;
|
||||
const http_vhostdb_backend_t *backend;
|
||||
buffer *b;
|
||||
stat_cache_entry *sce;
|
||||
|
||||
/* no host specified? */
|
||||
if (buffer_string_is_empty(con->uri.authority)) return HANDLER_GO_ON;
|
||||
|
||||
/* XXX: future: implement larger, managed cache
|
||||
* of database responses (positive and negative) */
|
||||
|
||||
/* check if cached this connection */
|
||||
ve = con->plugin_ctx[p->id];
|
||||
if (ve && buffer_is_equal(ve->server_name, con->uri.authority)) {
|
||||
return mod_vhostdb_found(con, ve); /* HANDLER_GO_ON */
|
||||
}
|
||||
|
||||
mod_vhostdb_patch_connection(srv, con, p);
|
||||
if (!p->conf.vhostdb_backend) return HANDLER_GO_ON;
|
||||
|
||||
b = p->tmp_buf;
|
||||
backend = p->conf.vhostdb_backend;
|
||||
if (0 != backend->query(srv, con, backend->p_d, b)) {
|
||||
return mod_vhostdb_error_500(con); /* HANDLER_FINISHED */
|
||||
}
|
||||
|
||||
if (buffer_string_is_empty(b)) {
|
||||
/* no such virtual host */
|
||||
return HANDLER_GO_ON;
|
||||
}
|
||||
|
||||
/* sanity check that really is a directory */
|
||||
buffer_append_slash(b);
|
||||
if (HANDLER_ERROR == stat_cache_get_entry(srv, con, b, &sce)) {
|
||||
log_error_write(srv, __FILE__, __LINE__, "sb", strerror(errno), b);
|
||||
return mod_vhostdb_error_500(con); /* HANDLER_FINISHED */
|
||||
}
|
||||
if (!S_ISDIR(sce->st.st_mode)) {
|
||||
log_error_write(srv, __FILE__, __LINE__, "sb", "Not a directory", b);
|
||||
return mod_vhostdb_error_500(con); /* HANDLER_FINISHED */
|
||||
}
|
||||
|
||||
/* cache the data */
|
||||
if (!ve) con->plugin_ctx[p->id] = ve = vhostdb_entry_init();
|
||||
buffer_copy_buffer(ve->server_name, con->uri.authority);
|
||||
buffer_copy_buffer(ve->document_root, b);
|
||||
|
||||
return mod_vhostdb_found(con, ve); /* HANDLER_GO_ON */
|
||||
}
|
||||
|
||||
int mod_vhostdb_plugin_init(plugin *p);
|
||||
int mod_vhostdb_plugin_init(plugin *p) {
|
||||
p->version = LIGHTTPD_VERSION_ID;
|
||||
p->name = buffer_init_string("vhostdb");
|
||||
p->init = mod_vhostdb_init;
|
||||
p->cleanup = mod_vhostdb_free;
|
||||
p->set_defaults = mod_vhostdb_set_defaults;
|
||||
p->handle_docroot = mod_vhostdb_handle_docroot;
|
||||
p->connection_reset = mod_vhostdb_handle_connection_close;
|
||||
|
||||
p->data = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,324 @@
|
|||
#include "first.h"
|
||||
|
||||
#include <dbi/dbi.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "base.h"
|
||||
#include "http_vhostdb.h"
|
||||
#include "log.h"
|
||||
#include "plugin.h"
|
||||
|
||||
/*
|
||||
* virtual host plugin using DBI for domain to directory lookups
|
||||
*
|
||||
* e.g.
|
||||
* vhostdb.dbi = ( "sql" => "SELECT docroot FROM vhosts WHERE host='?'"
|
||||
* "dbtype" => "sqlite3",
|
||||
* "dbname" => "mydb.sqlite",
|
||||
* "sqlite_dbdir" => "/path/to/sqlite/dbs/" )
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
dbi_conn dbconn;
|
||||
dbi_inst dbinst;
|
||||
buffer *sqlquery;
|
||||
server *srv;
|
||||
short reconnect_count;
|
||||
} vhostdb_config;
|
||||
|
||||
typedef struct {
|
||||
void *vdata;
|
||||
array *options;
|
||||
} plugin_config;
|
||||
|
||||
typedef struct {
|
||||
PLUGIN_DATA;
|
||||
plugin_config **config_storage;
|
||||
plugin_config conf;
|
||||
} plugin_data;
|
||||
|
||||
/* used to reconnect to the database when we get disconnected */
|
||||
static void mod_vhostdb_dbi_error_callback (dbi_conn dbconn, void *vdata)
|
||||
{
|
||||
vhostdb_config *dbconf = (vhostdb_config *)vdata;
|
||||
const char *errormsg = NULL;
|
||||
/*assert(dbconf->dbconn == dbconn);*/
|
||||
|
||||
while (++dbconf->reconnect_count <= 3) { /* retry */
|
||||
if (0 == dbi_conn_connect(dbconn)) {
|
||||
fd_close_on_exec(dbi_conn_get_socket(dbconn));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
dbi_conn_error(dbconn, &errormsg);
|
||||
log_error_write(dbconf->srv, __FILE__, __LINE__, "ss",
|
||||
"dbi_conn_connect():", errormsg);
|
||||
}
|
||||
|
||||
static void mod_vhostdb_dbconf_free (void *vdata)
|
||||
{
|
||||
vhostdb_config *dbconf = (vhostdb_config *)vdata;
|
||||
if (!dbconf) return;
|
||||
dbi_conn_close(dbconf->dbconn);
|
||||
dbi_shutdown_r(dbconf->dbinst);
|
||||
free(dbconf);
|
||||
}
|
||||
|
||||
static int mod_vhostdb_dbconf_setup (server *srv, array *opts, void **vdata)
|
||||
{
|
||||
buffer *sqlquery = NULL;
|
||||
const buffer *dbtype=NULL, *dbname=NULL;
|
||||
|
||||
for (size_t i = 0; i < opts->used; ++i) {
|
||||
const data_string *ds = (data_string *)opts->data[i];
|
||||
if (ds->type == TYPE_STRING) {
|
||||
if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("sql"))) {
|
||||
sqlquery = ds->value;
|
||||
} else if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("dbname"))) {
|
||||
dbname = ds->value;
|
||||
} else if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("dbtype"))) {
|
||||
dbtype = ds->value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* required:
|
||||
* - sql (sql query)
|
||||
* - dbtype
|
||||
* - dbname
|
||||
*
|
||||
* optional:
|
||||
* - username, some databases don't require this (sqlite)
|
||||
* - password, default: empty
|
||||
* - socket, default: database type default
|
||||
* - hostname, if set overrides socket
|
||||
* - port, default: database default
|
||||
* - encoding, default: database default
|
||||
*/
|
||||
|
||||
if (!buffer_string_is_empty(sqlquery)
|
||||
&& !buffer_is_empty(dbname) && !buffer_is_empty(dbtype)) {
|
||||
/* create/initialise database */
|
||||
vhostdb_config *dbconf;
|
||||
dbi_inst dbinst = NULL;
|
||||
dbi_conn dbconn;
|
||||
if (dbi_initialize_r(NULL, &dbinst) < 1) {
|
||||
log_error_write(srv, __FILE__, __LINE__, "s",
|
||||
"dbi_initialize_r() failed. "
|
||||
"Do you have the DBD for this db type installed?");
|
||||
return -1;
|
||||
}
|
||||
dbconn = dbi_conn_new_r(dbtype->ptr, dbinst);
|
||||
if (NULL == dbconn) {
|
||||
log_error_write(srv, __FILE__, __LINE__, "s",
|
||||
"dbi_conn_new_r() failed. "
|
||||
"Do you have the DBD for this db type installed?");
|
||||
dbi_shutdown_r(dbinst);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* set options */
|
||||
for (size_t j = 0; j < opts->used; ++j) {
|
||||
data_unset *du = opts->data[j];
|
||||
const buffer *opt = du->key;
|
||||
if (!buffer_string_is_empty(opt)) {
|
||||
if (du->type == TYPE_INTEGER) {
|
||||
data_integer *di = (data_integer *)du;
|
||||
dbi_conn_set_option_numeric(dbconn, opt->ptr, di->value);
|
||||
} else if (du->type == TYPE_STRING) {
|
||||
data_string *ds = (data_string *)du;
|
||||
if (ds->value != sqlquery && ds->value != dbtype) {
|
||||
dbi_conn_set_option(dbconn, opt->ptr, ds->value->ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dbconf = (vhostdb_config *)calloc(1, sizeof(*dbconf));
|
||||
dbconf->dbinst = dbinst;
|
||||
dbconf->dbconn = dbconn;
|
||||
dbconf->sqlquery = sqlquery;
|
||||
dbconf->srv = srv;
|
||||
dbconf->reconnect_count = 0;
|
||||
*vdata = dbconf;
|
||||
|
||||
/* used to automatically reconnect to the database */
|
||||
dbi_conn_error_handler(dbconn, mod_vhostdb_dbi_error_callback, dbconf);
|
||||
|
||||
/* connect to database */
|
||||
mod_vhostdb_dbi_error_callback(dbconn, dbconf);
|
||||
if (dbconf->reconnect_count >= 3) return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mod_vhostdb_patch_connection (server *srv, connection *con, plugin_data *p);
|
||||
|
||||
static int mod_vhostdb_dbi_query(server *srv, connection *con, void *p_d, buffer *docroot)
|
||||
{
|
||||
plugin_data *p = (plugin_data *)p_d;
|
||||
vhostdb_config *dbconf;
|
||||
dbi_result result;
|
||||
unsigned long long nrows;
|
||||
int retry_count = 0;
|
||||
|
||||
/*(reuse buffer for sql query before generating docroot result)*/
|
||||
buffer *sqlquery = docroot;
|
||||
buffer_string_set_length(sqlquery, 0); /*(also resets docroot (alias))*/
|
||||
|
||||
mod_vhostdb_patch_connection(srv, con, p);
|
||||
if (NULL == p->conf.vdata) return 0; /*(after resetting docroot)*/
|
||||
dbconf = (vhostdb_config *)p->conf.vdata;
|
||||
|
||||
for (char *b = dbconf->sqlquery->ptr, *d; *b; b = d+1) {
|
||||
if (NULL != (d = strchr(b, '?'))) {
|
||||
/* escape the uri.authority */
|
||||
char *esc = NULL;
|
||||
size_t len = dbi_conn_escape_string_copy(dbconf->dbconn, con->uri.authority->ptr, &esc);
|
||||
buffer_append_string_len(sqlquery, b, (size_t)(d - b));
|
||||
buffer_append_string_len(sqlquery, esc, len);
|
||||
free(esc);
|
||||
if (0 == len) return -1;
|
||||
} else {
|
||||
d = dbconf->sqlquery->ptr + buffer_string_length(dbconf->sqlquery);
|
||||
buffer_append_string_len(sqlquery, b, (size_t)(d - b));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* reset our reconnect-attempt counter, this is a new query. */
|
||||
dbconf->reconnect_count = 0;
|
||||
|
||||
do {
|
||||
result = dbi_conn_query(dbconf->dbconn, sqlquery->ptr);
|
||||
} while (!result && ++retry_count < 2);
|
||||
|
||||
buffer_string_set_length(docroot, 0); /*(reset buffer to store result)*/
|
||||
|
||||
if (!result) {
|
||||
const char *errmsg;
|
||||
dbi_conn_error(dbconf->dbconn, &errmsg);
|
||||
log_error_write(srv, __FILE__, __LINE__, "s", errmsg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
nrows = dbi_result_get_numrows(result);
|
||||
if (nrows && nrows != DBI_ROW_ERROR && dbi_result_next_row(result)) {
|
||||
buffer_copy_string(docroot, dbi_result_get_string_idx(result, 1));
|
||||
} /* else no such virtual host */
|
||||
|
||||
dbi_result_free(result);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
INIT_FUNC(mod_vhostdb_init) {
|
||||
static http_vhostdb_backend_t http_vhostdb_backend_dbi =
|
||||
{ "dbi", mod_vhostdb_dbi_query, NULL };
|
||||
plugin_data *p = calloc(1, sizeof(*p));
|
||||
|
||||
/* register http_vhostdb_backend_dbi */
|
||||
http_vhostdb_backend_dbi.p_d = p;
|
||||
http_vhostdb_backend_set(&http_vhostdb_backend_dbi);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
FREE_FUNC(mod_vhostdb_cleanup) {
|
||||
plugin_data *p = p_d;
|
||||
if (!p) return HANDLER_GO_ON;
|
||||
|
||||
if (p->config_storage) {
|
||||
for (size_t i = 0; i < srv->config_context->used; i++) {
|
||||
plugin_config *s = p->config_storage[i];
|
||||
if (!s) continue;
|
||||
mod_vhostdb_dbconf_free(s->vdata);
|
||||
array_free(s->options);
|
||||
free(s);
|
||||
}
|
||||
free(p->config_storage);
|
||||
}
|
||||
free(p);
|
||||
|
||||
UNUSED(srv);
|
||||
return HANDLER_GO_ON;
|
||||
}
|
||||
|
||||
SETDEFAULTS_FUNC(mod_vhostdb_set_defaults) {
|
||||
plugin_data *p = p_d;
|
||||
|
||||
config_values_t cv[] = {
|
||||
{ "vhostdb.dbi", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },
|
||||
{ NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
|
||||
};
|
||||
|
||||
p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
|
||||
|
||||
for (size_t i = 0; i < srv->config_context->used; ++i) {
|
||||
data_config const *config = (data_config const*)srv->config_context->data[i];
|
||||
plugin_config *s = calloc(1, sizeof(plugin_config));
|
||||
|
||||
s->options = array_init();
|
||||
cv[0].destination = s->options;
|
||||
|
||||
p->config_storage[i] = s;
|
||||
|
||||
if (config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) {
|
||||
return HANDLER_ERROR;
|
||||
}
|
||||
|
||||
if (s->options->used
|
||||
&& 0 != mod_vhostdb_dbconf_setup(srv, s->options, &s->vdata)) {
|
||||
return HANDLER_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return HANDLER_GO_ON;
|
||||
}
|
||||
|
||||
#define PATCH(x) \
|
||||
p->conf.x = s->x;
|
||||
static void mod_vhostdb_patch_connection (server *srv, connection *con, plugin_data *p)
|
||||
{
|
||||
plugin_config *s = p->config_storage[0];
|
||||
PATCH(vdata);
|
||||
|
||||
/* skip the first, the global context */
|
||||
for (size_t i = 1; i < srv->config_context->used; ++i) {
|
||||
data_config *dc = (data_config *)srv->config_context->data[i];
|
||||
s = p->config_storage[i];
|
||||
|
||||
/* condition didn't match */
|
||||
if (!config_check_cond(srv, con, dc)) continue;
|
||||
|
||||
/* merge config */
|
||||
for (size_t j = 0; j < dc->value->used; ++j) {
|
||||
data_unset *du = dc->value->data[j];
|
||||
|
||||
if (buffer_is_equal_string(du->key, CONST_STR_LEN("vhostdb.dbi"))) {
|
||||
PATCH(vdata);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#undef PATCH
|
||||
|
||||
/* this function is called at dlopen() time and inits the callbacks */
|
||||
int mod_vhostdb_dbi_plugin_init (plugin *p);
|
||||
int mod_vhostdb_dbi_plugin_init (plugin *p)
|
||||
{
|
||||
p->version = LIGHTTPD_VERSION_ID;
|
||||
p->name = buffer_init_string("vhostdb_dbi");
|
||||
|
||||
p->init = mod_vhostdb_init;
|
||||
p->cleanup = mod_vhostdb_cleanup;
|
||||
p->set_defaults = mod_vhostdb_set_defaults;
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,512 @@
|
|||
#include "first.h"
|
||||
|
||||
#include <ldap.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "base.h"
|
||||
#include "http_vhostdb.h"
|
||||
#include "log.h"
|
||||
#include "plugin.h"
|
||||
|
||||
/*
|
||||
* virtual host plugin using LDAP for domain to directory lookups
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
LDAP *ldap;
|
||||
buffer *filter;
|
||||
|
||||
const char *attr;
|
||||
const char *host;
|
||||
const char *basedn;
|
||||
const char *binddn;
|
||||
const char *bindpw;
|
||||
const char *cafile;
|
||||
unsigned short starttls;
|
||||
} vhostdb_config;
|
||||
|
||||
typedef struct {
|
||||
void *vdata;
|
||||
array *options;
|
||||
} plugin_config;
|
||||
|
||||
typedef struct {
|
||||
PLUGIN_DATA;
|
||||
plugin_config **config_storage;
|
||||
plugin_config conf;
|
||||
} plugin_data;
|
||||
|
||||
static void mod_vhostdb_dbconf_free (void *vdata)
|
||||
{
|
||||
vhostdb_config *dbconf = (vhostdb_config *)vdata;
|
||||
if (!dbconf) return;
|
||||
if (NULL != dbconf->ldap) ldap_unbind_ext_s(dbconf->ldap, NULL, NULL);
|
||||
free(dbconf);
|
||||
}
|
||||
|
||||
static int mod_vhostdb_dbconf_setup (server *srv, array *opts, void **vdata)
|
||||
{
|
||||
buffer *filter = NULL;
|
||||
const char *attr = "documentRoot";
|
||||
const char *basedn=NULL,*binddn=NULL,*bindpw=NULL,*host=NULL,*cafile=NULL;
|
||||
unsigned short starttls = 0;
|
||||
|
||||
for (size_t i = 0; i < opts->used; ++i) {
|
||||
const data_string *ds = (data_string *)opts->data[i];
|
||||
if (ds->type == TYPE_STRING) {
|
||||
if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("filter"))) {
|
||||
filter = ds->value;
|
||||
} else if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("attr"))) {
|
||||
if (!buffer_string_is_empty(ds->value)) attr = ds->value->ptr;
|
||||
} else if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("host"))) {
|
||||
host = ds->value->ptr;
|
||||
} else if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("base-dn"))) {
|
||||
if (!buffer_string_is_empty(ds->value)) basedn = ds->value->ptr;
|
||||
} else if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("bind-dn"))) {
|
||||
if (!buffer_string_is_empty(ds->value)) binddn = ds->value->ptr;
|
||||
} else if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("bind-pw"))) {
|
||||
bindpw = ds->value->ptr;
|
||||
} else if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("ca-file"))) {
|
||||
if (!buffer_string_is_empty(ds->value)) cafile = ds->value->ptr;
|
||||
} else if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("starttls"))) {
|
||||
starttls = !buffer_is_equal_string(ds->value, CONST_STR_LEN("disable"))
|
||||
&& !buffer_is_equal_string(ds->value, CONST_STR_LEN("0"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* required:
|
||||
* - host
|
||||
* - filter (LDAP query)
|
||||
* - base-dn
|
||||
*
|
||||
* optional:
|
||||
* - attr (LDAP attribute with docroot; default "documentRoot")
|
||||
* - bind-dn
|
||||
* - bind-pw
|
||||
* - ca-file
|
||||
* - starttls
|
||||
*/
|
||||
|
||||
if (!buffer_string_is_empty(filter) && NULL != host && NULL != basedn) {
|
||||
vhostdb_config *dbconf;
|
||||
|
||||
if (NULL == strchr(filter->ptr, '?')) {
|
||||
log_error_write(srv, __FILE__, __LINE__, "s",
|
||||
"ldap: filter is missing a replace-operator '?'");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* openldap sets FD_CLOEXEC on database socket descriptors
|
||||
* (still race between creation of socket and fcntl FD_CLOEXEC)
|
||||
* (YMMV with other LDAP client libraries) */
|
||||
|
||||
dbconf = (vhostdb_config *)calloc(1, sizeof(*dbconf));
|
||||
dbconf->ldap = NULL;
|
||||
dbconf->filter = filter;
|
||||
dbconf->attr = attr;
|
||||
dbconf->host = host;
|
||||
dbconf->basedn = basedn;
|
||||
dbconf->binddn = binddn;
|
||||
dbconf->bindpw = bindpw;
|
||||
dbconf->cafile = cafile;
|
||||
dbconf->starttls = starttls;
|
||||
*vdata = dbconf;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note: a large portion of the LDAP code is copied verbatim from mod_authn_ldap
|
||||
* with only changes being use of vhostdb_config instead of plugin_config struct
|
||||
* and (const char *) strings in vhostdb_config instead of (buffer *).
|
||||
*/
|
||||
|
||||
static void mod_authn_ldap_err(server *srv, const char *file, unsigned long line, const char *fn, int err)
|
||||
{
|
||||
log_error_write(srv,file,line,"sSss","ldap:",fn,":",ldap_err2string(err));
|
||||
}
|
||||
|
||||
static void mod_authn_ldap_opt_err(server *srv, const char *file, unsigned long line, const char *fn, LDAP *ld)
|
||||
{
|
||||
int err;
|
||||
ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &err);
|
||||
mod_authn_ldap_err(srv, file, line, fn, err);
|
||||
}
|
||||
|
||||
static void mod_authn_append_ldap_filter_escape(buffer * const filter, const buffer * const raw) {
|
||||
/* [RFC4515] 3. String Search Filter Definition
|
||||
*
|
||||
* [...]
|
||||
*
|
||||
* The <valueencoding> rule ensures that the entire filter string is a
|
||||
* valid UTF-8 string and provides that the octets that represent the
|
||||
* ASCII characters "*" (ASCII 0x2a), "(" (ASCII 0x28), ")" (ASCII
|
||||
* 0x29), "\" (ASCII 0x5c), and NUL (ASCII 0x00) are represented as a
|
||||
* backslash "\" (ASCII 0x5c) followed by the two hexadecimal digits
|
||||
* representing the value of the encoded octet.
|
||||
*
|
||||
* [...]
|
||||
*
|
||||
* As indicated by the <valueencoding> rule, implementations MUST escape
|
||||
* all octets greater than 0x7F that are not part of a valid UTF-8
|
||||
* encoding sequence when they generate a string representation of a
|
||||
* search filter. Implementations SHOULD accept as input strings that
|
||||
* are not valid UTF-8 strings. This is necessary because RFC 2254 did
|
||||
* not clearly define the term "string representation" (and in
|
||||
* particular did not mention that the string representation of an LDAP
|
||||
* search filter is a string of UTF-8-encoded Unicode characters).
|
||||
*
|
||||
*
|
||||
* https://www.ldap.com/ldap-filters
|
||||
* Although not required, you may escape any other characters that you want
|
||||
* in the assertion value (or substring component) of a filter. This may be
|
||||
* accomplished by prefixing the hexadecimal representation of each byte of
|
||||
* the UTF-8 encoding of the character to escape with a backslash character.
|
||||
*/
|
||||
const char * const b = raw->ptr;
|
||||
const size_t rlen = buffer_string_length(raw);
|
||||
for (size_t i = 0; i < rlen; ++i) {
|
||||
size_t len = i;
|
||||
char *f;
|
||||
do {
|
||||
/* encode all UTF-8 chars with high bit set
|
||||
* (instead of validating UTF-8 and escaping only invalid UTF-8) */
|
||||
if (((unsigned char *)b)[len] > 0x7f)
|
||||
break;
|
||||
switch (b[len]) {
|
||||
default:
|
||||
continue;
|
||||
case '\0': case '(': case ')': case '*': case '\\':
|
||||
break;
|
||||
}
|
||||
break;
|
||||
} while (++len < rlen);
|
||||
len -= i;
|
||||
|
||||
if (len) {
|
||||
buffer_append_string_len(filter, b+i, len);
|
||||
if ((i += len) == rlen) break;
|
||||
}
|
||||
|
||||
/* escape * ( ) \ NUL ('\0') (and all UTF-8 chars with high bit set) */
|
||||
buffer_string_prepare_append(filter, 3);
|
||||
f = filter->ptr + buffer_string_length(filter);
|
||||
f[0] = '\\';
|
||||
f[1] = "0123456789abcdef"[(((unsigned char *)b)[i] >> 4) & 0xf];
|
||||
f[2] = "0123456789abcdef"[(((unsigned char *)b)[i] ) & 0xf];
|
||||
buffer_commit(filter, 3);
|
||||
}
|
||||
}
|
||||
|
||||
static LDAP * mod_authn_ldap_host_init(server *srv, vhostdb_config *s) {
|
||||
LDAP *ld;
|
||||
int ret;
|
||||
|
||||
ld = ldap_init(s->host, LDAP_PORT);
|
||||
if (NULL == ld) {
|
||||
log_error_write(srv, __FILE__, __LINE__, "sss", "ldap:", "ldap_init():",
|
||||
strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = LDAP_VERSION3;
|
||||
ret = ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &ret);
|
||||
if (LDAP_OPT_SUCCESS != ret) {
|
||||
mod_authn_ldap_err(srv, __FILE__, __LINE__, "ldap_set_options()", ret);
|
||||
ldap_memfree(ld);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (s->starttls) {
|
||||
/* if no CA file is given, it is ok, as we will use encryption
|
||||
* if the server requires a CAfile it will tell us */
|
||||
if (s->cafile) {
|
||||
ret = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE, s->cafile);
|
||||
if (LDAP_OPT_SUCCESS != ret) {
|
||||
mod_authn_ldap_err(srv, __FILE__, __LINE__,
|
||||
"ldap_set_option(LDAP_OPT_X_TLS_CACERTFILE)",
|
||||
ret);
|
||||
ldap_memfree(ld);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
ret = ldap_start_tls_s(ld, NULL, NULL);
|
||||
if (LDAP_OPT_SUCCESS != ret) {
|
||||
mod_authn_ldap_err(srv,__FILE__,__LINE__,"ldap_start_tls_s()",ret);
|
||||
ldap_memfree(ld);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return ld;
|
||||
}
|
||||
|
||||
static int mod_authn_ldap_bind(server *srv, LDAP *ld, const char *dn, const char *pw) {
|
||||
#if 0
|
||||
struct berval creds;
|
||||
int ret;
|
||||
|
||||
if (NULL != pw) {
|
||||
*((const char **)&creds.bv_val) = pw; /*(cast away const)*/
|
||||
creds.bv_len = strlen(pw);
|
||||
} else {
|
||||
creds.bv_val = NULL;
|
||||
creds.bv_len = 0;
|
||||
}
|
||||
|
||||
/* RFE: add functionality: LDAP_SASL_EXTERNAL (or GSS-SPNEGO, etc.) */
|
||||
|
||||
ret = ldap_sasl_bind_s(ld,dn,LDAP_SASL_SIMPLE,&creds,NULL,NULL,NULL);
|
||||
if (ret != LDAP_SUCCESS) {
|
||||
mod_authn_ldap_err(srv, __FILE__, __LINE__, "ldap_sasl_bind_s()", ret);
|
||||
}
|
||||
#else
|
||||
int ret = ldap_simple_bind_s(ld, dn, pw);
|
||||
if (ret != LDAP_SUCCESS) {
|
||||
mod_authn_ldap_err(srv, __FILE__, __LINE__, "ldap_simple_bind_s()",ret);
|
||||
}
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static LDAPMessage * mod_authn_ldap_search(server *srv, vhostdb_config *s, char *base, char *filter) {
|
||||
LDAPMessage *lm = NULL;
|
||||
char *attrs[] = { LDAP_NO_ATTRS, NULL };
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* 1. connect anonymously (if not already connected)
|
||||
* (ldap connection is kept open unless connection-level error occurs)
|
||||
* 2. issue search using filter
|
||||
*/
|
||||
|
||||
if (s->ldap != NULL) {
|
||||
ret = ldap_search_ext_s(s->ldap, base, LDAP_SCOPE_SUBTREE, filter,
|
||||
attrs, 0, NULL, NULL, NULL, 0, &lm);
|
||||
if (LDAP_SUCCESS == ret) {
|
||||
return lm;
|
||||
} else if (LDAP_SERVER_DOWN != ret) {
|
||||
/* try again (or initial request);
|
||||
* ldap lib sometimes fails for the first call but reconnects */
|
||||
ret = ldap_search_ext_s(s->ldap, base, LDAP_SCOPE_SUBTREE, filter,
|
||||
attrs, 0, NULL, NULL, NULL, 0, &lm);
|
||||
if (LDAP_SUCCESS == ret) {
|
||||
return lm;
|
||||
}
|
||||
}
|
||||
|
||||
ldap_unbind_ext_s(s->ldap, NULL, NULL);
|
||||
}
|
||||
|
||||
s->ldap = mod_authn_ldap_host_init(srv, s);
|
||||
if (NULL == s->ldap) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = mod_authn_ldap_bind(srv, s->ldap, s->binddn, s->bindpw);
|
||||
if (LDAP_SUCCESS != ret) {
|
||||
ldap_memfree(s->ldap);
|
||||
s->ldap = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = ldap_search_ext_s(s->ldap, base, LDAP_SCOPE_SUBTREE, filter,
|
||||
attrs, 0, NULL, NULL, NULL, 0, &lm);
|
||||
if (LDAP_SUCCESS != ret) {
|
||||
log_error_write(srv, __FILE__, __LINE__, "sSss",
|
||||
"ldap:", ldap_err2string(ret), "; filter:", filter);
|
||||
ldap_unbind_ext_s(s->ldap, NULL, NULL);
|
||||
s->ldap = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return lm;
|
||||
}
|
||||
|
||||
static void mod_vhostdb_patch_connection (server *srv, connection *con, plugin_data *p);
|
||||
|
||||
static int mod_vhostdb_ldap_query(server *srv, connection *con, void *p_d, buffer *docroot)
|
||||
{
|
||||
plugin_data *p = (plugin_data *)p_d;
|
||||
vhostdb_config *dbconf;
|
||||
LDAP *ld;
|
||||
LDAPMessage *lm, *first;
|
||||
struct berval **vals;
|
||||
int count;
|
||||
char *basedn;
|
||||
buffer *template;
|
||||
|
||||
/*(reuse buffer for ldap query before generating docroot result)*/
|
||||
buffer *filter = docroot;
|
||||
buffer_string_set_length(filter, 0); /*(also resets docroot (alias))*/
|
||||
|
||||
mod_vhostdb_patch_connection(srv, con, p);
|
||||
if (NULL == p->conf.vdata) return 0; /*(after resetting docroot)*/
|
||||
dbconf = (vhostdb_config *)p->conf.vdata;
|
||||
|
||||
template = dbconf->filter;
|
||||
for (char *b = template->ptr, *d; *b; b = d+1) {
|
||||
if (NULL != (d = strchr(b, '?'))) {
|
||||
buffer_append_string_len(filter, b, (size_t)(d - b));
|
||||
mod_authn_append_ldap_filter_escape(filter, con->uri.authority);
|
||||
} else {
|
||||
d = template->ptr + buffer_string_length(template);
|
||||
buffer_append_string_len(filter, b, (size_t)(d - b));
|
||||