servers/slapd/slappasswd
servers/slapd/slapschema
servers/slapd/slaptest
+servers/lloadd/lloadd
tests/progs/ldif-filter
tests/progs/slapd-addel
tests/progs/slapd-bind
SLAPD_NDB_LIBS = @SLAPD_NDB_LIBS@
WT_LIBS = @WT_LIBS@
+LEVENT_LIBS = @LEVENT_LIBS@
+
LDAP_LIBLBER_LA = $(LDAP_LIBDIR)/liblber/liblber.la
LDAP_LIBLDAP_LA = $(LDAP_LIBDIR)/libldap/libldap.la
$(LDAP_LIBLDAP_LA) $(LDAP_LIBLBER_LA)
SLAPD_L = $(LDAP_LIBLUNICODE_A) $(LDAP_LIBREWRITE_A) \
$(LDAP_LIBLUTIL_A) $(LDAP_LIBLDAP_LA) $(LDAP_LIBLBER_LA)
+LLOADD_L = $(LDAP_LIBLUTIL_A) $(LDAP_LIBLDAP_LA) \
+ $(LDAP_LIBLBER_LA)
WRAP_LIBS = @WRAP_LIBS@
# AutoConfig generated
SLAPD_SQL_LIBS = @SLAPD_SQL_LIBS@
SLAPD_LIBS = @SLAPD_LIBS@ @SLAPD_PERL_LDFLAGS@ @SLAPD_SQL_LDFLAGS@ @SLAPD_SQL_LIBS@ @SLAPD_SLP_LIBS@ @SLAPD_GMP_LIBS@
+LLOADD_LIBS = @BALANCER_LIBS@ $(LEVENT_LIBS)
# Our Defaults
CC = $(AC_CC)
OL_ARG_ENABLE(valsort, [AS_HELP_STRING([--enable-valsort], [Value Sorting overlay])],
no, [no yes mod], ol_enable_overlays)
+dnl ----------------------------------------------------------------
+dnl BALANCER OPTIONS
+AC_ARG_ENABLE(balanceroptions,[
+LLOADD (Load Balancer Daemon) Options:])
+OL_ARG_ENABLE(balancer,[ --enable-balancer enable building load balancer], auto)dnl
+
dnl ----------------------------------------------------------------
AC_ARG_ENABLE(xxliboptions,[
Library Generation & Linking Options])
ol_enable_dynamic=yes
fi
+if test $ol_enable_balancer = yes ; then
+ dnl Load Balancer was specifically enabled
+ if test $ol_with_threads = no ; then
+ AC_MSG_ERROR([Load balancer requires threads])
+ fi
+fi
+
if test $ol_enable_spasswd = yes ; then
if test $ol_with_cyrus_sasl = no ; then
AC_MSG_ERROR([--enable-spasswd requires --with-cyrus-sasl])
SLAPD_NDB_LIBS=
SLAPD_NDB_INCS=
LTHREAD_LIBS=
+LEVENT_LIBS=
LUTIL_LIBS=
CLIENT_LIBS=
SLAPD_LIBS=
+BALANCER_LIBS=
BUILD_SLAPD=no
+BUILD_BALANCER=no
BUILD_THREAD=no
fi
fi
+dnl ----------------------------------------------------------------
+dnl Libevent
+if test $ol_enable_balancer != no ; then
+ AC_CHECK_LIB(event_core, evconnlistener_set_error_cb,
+ [have_libevent=yes
+ LEVENT_LIBS="$LEVENT_LIBS -levent_core"],
+ [AC_CHECK_LIB(event, evconnlistener_set_error_cb,
+ [have_libevent=yes
+ LEVENT_LIBS="$LEVENT_LIBS -levent"],
+ [have_libevent=no])])
+
+ if test $have_libevent = yes ; then
+ AC_DEFINE(HAVE_LIBEVENT, 1, [define if you have -levent])
+ elif test $ol_enable_balancer = yes ; then
+ AC_MSG_ERROR([You need libevent 2.0 or later to build the load balancer])
+ fi
+fi
+
dnl ----------------------------------------------------------------
dnl Checks for typedefs, structures, and compiler characteristics.
AC_DEFINE_UNQUOTED(SLAPD_OVER_VALSORT,$MFLAG,[define for Value Sorting overlay])
fi
+if test "$ol_enable_balancer" != no \
+ -a "$ol_with_threads" != no \
+ -a "$have_libevent" = yes ; then
+ BUILD_BALANCER=yes
+fi
+
if test "$ol_enable_slapi" != no ; then
AC_DEFINE(ENABLE_SLAPI,1,[define to enable slapi library])
BUILD_SLAPI=yes
AC_SUBST(BUILD_TRANSLUCENT)
AC_SUBST(BUILD_UNIQUE)
AC_SUBST(BUILD_VALSORT)
+ AC_SUBST(BUILD_BALANCER)
AC_SUBST(LDAP_LIBS)
AC_SUBST(CLIENT_LIBS)
AC_SUBST(SLAPD_LIBS)
+AC_SUBST(BALANCER_LIBS)
AC_SUBST(SLAPD_NDB_LIBS)
AC_SUBST(SLAPD_NDB_INCS)
AC_SUBST(LTHREAD_LIBS)
AC_SUBST(LUTIL_LIBS)
+AC_SUBST(LEVENT_LIBS)
AC_SUBST(WRAP_LIBS)
AC_SUBST(SLAPD_MODULES_CPPFLAGS)
[servers/slapd/shell-backends/Makefile:build/top.mk:servers/slapd/shell-backends/Makefile.in:build/srv.mk]
[servers/slapd/slapi/Makefile:build/top.mk:servers/slapd/slapi/Makefile.in:build/lib.mk:build/lib-shared.mk]
[servers/slapd/overlays/Makefile:build/top.mk:servers/slapd/overlays/Makefile.in:build/lib.mk]
+[servers/lloadd/Makefile:build/top.mk:servers/lloadd/Makefile.in:build/srv.mk]
[tests/Makefile:build/top.mk:tests/Makefile.in:build/dir.mk]
[tests/run]
[tests/progs/Makefile:build/top.mk:tests/progs/Makefile.in:build/rules.mk])
--- /dev/null
+TODO:
+- [ ] keep a global op in-flight counter? (might need locking)
+- [-] scheduling (who does what, more than one select thread? How does the proxy
+ work get distributed between threads?)
+- [ ] managing timeouts?
+- [X] outline locking policy: seems like there might be a lock inversion in the
+ design looming: when working with op, might need a lock on both client and
+ upstream but depending on where we started, we might want to start with
+ locking one, then other
+- [ ] how to deal with the balancer running out of fds? Especially when we hit
+ the limit, then lose an upstream connection and accept() a client, we
+ wouldn't be able to initiate a new one. A bit of a DoS... But probably not
+ a concern for Ericsson
+- [ ] non-Linux? No idea how anything other than poll works (moot if building a
+ libevent/libuv-based load balancer since they take care of that, except
+ edge-triggered I/O?)
+- [-] rootDSE? Controls and exops might have different semantics and need
+ binding to the same upstream connection.
+- [ ] Just piggybacking on OpenLDAP as a module? Would still need some updates
+ in the core and the module/subsystem would be a very invasive one. On the
+ other hand, allows to expose live configuration and monitoring over LDAP
+ over the current slapd listeners without re-inventing the wheel.
+
+
+Expecting to handle only LDAPv3
+
+terms:
+ server - configured target
+ upstream - a single connection to a server
+ client - an incoming connection
+
+To maintain fairness `G( requested => ( F( progressed | failed ) ) )`, use
+queues and put timeouts in
+
+Runtime organisation
+------
+- main thread with its own event base handling signals
+- one thread (later possibly more) listening on the rendezvous sockets, handing
+ the new sockets to worker threads
+- n worker threads dealing with client and server I/O (dispatching actual work
+ to the thread pool most likely)
+- a thread pool to handle actual work
+
+Operational behaviour
+------
+
+- client read -> upstream write:
+ - client read:
+ - if TLS_SETUP, keep processing, set state back when finished and note that
+ we're under TLS
+ - ber_get_next(), if we don't have a tag, finished (unless we have true
+ edge-triggered I/O, also put the fd back into the ones we're waiting for)
+ - peek at op tag:
+ - unbind:
+ - with a single lock, mark all pending ops in upstreams abandoned, clear
+ client link (would it be fast enough if we remove them from upstream
+ map instead?)
+ - locked per op:
+ - remove op from upstream map
+ - check upstream is not write-suspended, if it is ...
+ - try to write the abandon op to upstream, suspend upstream if not
+ fully sent
+ - remove op from client map (how if we're in avl_apply?, another pass?)
+ - would be nice if we could wipe the complete client map then, otherwise
+ we need to queue it to have it freed when all abandons get passed onto
+ the upstream (just dropping them might put extra strain on upstreams,
+ will probably have a queue on each client/upstream anyway, not just a
+ single Ber)
+ - bind:
+ - check mechanism is not EXTERNAL (or implement it)
+ - abandon existing ops (see unbind)
+ - set state to BINDING, put DN into authzid
+ - pick upstream, create PDU and sent
+ - abandon:
+ - find op, mark for abandon, send to appropriate upstream
+ - Exop:
+ - check not BINDING (unless it's a cancel?)
+ - check OID:
+ - STARTTLS:
+ - check we don't have TLS yet
+ - abandon all
+ - set state to TLS_SETUP
+ - send the hello
+ - VC(?):
+ - similar to bind except for the abandons/state change
+ - other:
+ - check not BINDING
+ - pick an upstream
+ - create a PDU, send (marking upstream suspended if not written in full)
+ - check if should read again (keep a counter of number of times to read
+ off a connection in a single pass so that we maintain fairness)
+ - if read enough requests and can still read, re-queue ourselves (if we
+ don't have true edge-triggered I/O, we can just register the fd again)
+ - upstream write (only when suspended):
+ - flush the current BER
+ - there shouldn't be anything else?
+- upstream read -> client write:
+ - upstream read:
+ - ber_get_next(), if we don't have a tag, finished (unless we have true
+ edge-triggered I/O, also put the fd back into the ones we're waiting for)
+ - when we get it, peek at msgid, resolve client connection, lock, check:
+ - if unsolicited, handle as close (and mark connection closing)
+ - if op is abandoned or does not exist, drop PDU and op, update counters
+ - if client backlogged, suspend upstream, register callback to unsuspend
+ (on progress when writing to client or abandon from client (connection
+ death, abandon proper, ...))
+ - reconstruct final PDU, write BER to client, if did not write fully,
+ suspend client
+ - if a final response, decrement operation counts on upstream and client
+ - check if should read again (keep a counter of number of responses to read
+ off a connection in a single pass so that we don't starve any?)
+ - client write ready (only checked for when suspended):
+ - write the rest of pending BER if any
+ - on successful write, pick all pending ops that need failure response, push
+ to client (are there any controls that need to be present in response even
+ in the case of failure?, what to do with them?)
+ - on successfully flushing them, walk through suspended upstreams, picking
+ the pending PDU (unsuspending the upstream) and writing, if PDU flushed
+ successfully, pick next upstream
+ - if we successfully flushed all suspended upstreams, unsuspend client
+ (and disable the write callback)
+- upstream close/error:
+ - look up pending ops, try to write to clients, mark clients suspended that
+ have ops that need responses (another queue associated with client to speed
+ up?)
+ - schedule a new connection open
+- client close/error:
+ - same as unbind
+- client inactive (no pending ops and nothing happened in x seconds)
+ - might just send notice of disconnection and close
+- op timeout handling:
+ - mark for abandon
+ - send abandon
+ - send timeLimitExceeded/adminLimitExceeded to client
+
+Picking an upstream:
+- while there is a level available:
+ - pick a random ordering of upstreams based on weights
+ - while there is an upstream in the level:
+ - check number of ops in-flight (this is where we lock the upstream map)
+ - find the least busy connection (and check if a new connection should be
+ opened)
+ - try to lock for socket write, if available (no BER queued) we have our
+ upstream
+
+PDU processing:
+- request (have an upstream selected):
+ - get new msgid from upstream
+ - create an Op structure (actually, with the need for freelist lock, we can
+ make it a cache for freed operation structures, avoiding some malloc
+ traffic, to reset, we need slap_sl_mem_create( ,,, 1 ))
+ - check proxyauthz is not present? or just let upstream reject it if there are
+ two?
+ - add own controls at the end:
+ - construct proxyauthz from authzid
+ - construct session tracking from remote IP, own name, authzid
+ - send over
+ - insert Op into client and upstream maps
+- response/intermediate/entry:
+ - look up Op in upstream's map
+ - write old msgid, rest of the response can go unchanged
+ - if a response, remove Op from all maps (client and upstream)
+
+Managing upstreams:
+- async connect up to min_connections (is there a point in having a connection
+ count range if we can't use it when needed since all of the below is async?)
+- when connected, set up TLS (if requested)
+- when done, send a bind
+- go for the bind interaction
+- when done, add it to the upstream's connection list
+- (if a connection is suspended or connections are over 75 % op limit, schedule
+ creating a new connection setup unless connection limit has been hit)
+
+Managing timeouts:
+- two options:
+ - maintain a separate locked priority queue to give a perfect ordering to when
+ each operation is to time out, would need to maintain yet another place
+ where operations can be found.
+ - the locking protocol for disposing of the operation would need to be
+ adjusted and might become even more complicated, might do the alternative
+ initially and then attempt this if it helps performance
+ - just do a sweep over all clients (that mutex is less contended) every so
+ often. With many in-flight operations might be a lot of wasted work.
+ - we still need to sweep over all clients to check if they should be killed
+ anyway
+
+Dispatcher thread (2^n of them, fd x is handled by thread no x % (2^n)):
+- poll on all registered fds
+- remove each fd that's ready from the registered list and schedule the work
+- work threads can put their fd back in if they deem necessary (=not suspended)
+- this works as a poor man's edge-triggered polling, with enough workers, should
+ we do proper edge triggered I/O? What about non-Linux?
+
+Listener thread:
+- slapd has just one, which then reassigns the sockets to separate I/O
+ threads
+
+Threading:
+- if using slap_sl_malloc, how much perf do we gain? To allocate a context per
+ op, we should have a dedicated parent context so that when we free it, we can
+ use that exclusively. The parent context's parent would be the main thread's
+ context. This implies a lot of slap_sl_mem_setctx/slap_sl_mem_create( ,,, 0 )
+ and making sure an op does not allocate/free things from two threads at the
+ same time (might need an Op mutex after all? Not such a huge cost if we
+ routinely reuse Op structures)
+
+Locking policy:
+- read mutexes are unnecessary, we only have one thread receiving data from the
+ connection - the one started from the dispatcher
+- two reference counters of operation structures (an op is accessible from
+ client and upstream map, each counter is consistent when thread has a lock on
+ corresponding map), when decreasing the counter to zero, start freeing
+ procedure
+- place to mark disposal finished for each side, consistency enforced by holding
+ the freelist lock when reading/manipulating
+- when op is created, we already have a write lock on upstream socket and map,
+ start writing, insert to upstream map with upstream refcount 1, unlock, lock
+ client, insert (client refcount 0), unlock, lock upstream, decrement refcount
+ (triggers a test if we need to drop it now), unlock upstream, done
+- when upstream processes a PDU, locks its map, increments counter, (potentially
+ removes if it's a response), unlocks, locks client's map, write mutex (this
+ order?) and full client mutex (if a bind response)
+- when client side wants to work with a PDU (abandon, (un)bind), locks its map,
+ increase refcount, unlocks, locks upstream map, write mutex, sends or queues
+ abandon, unlocks write mutex, initiates freeing procedure from upstream side
+ (or if having to remember we've already increased client-side refcount, mark
+ for deletion, lose upstream lock, lock client, decref, either triggering
+ deletion from client or mark for it)
+- if we have operation lock, we can simplify a bit (no need for three-stage
+ locking above)
+
+Shutdown:
+- stop accept() thread(s) - potentially add a channel to hand these listening
+ sockets over for zero-downtime restart
+- if very gentle, mark connections as closing, start timeout and:
+ - when a new non-abandon PDU comes in from client - return LDAP_UNAVAILABLE
+ - when receiving a PDU from upstream, send over to client, if no ops pending,
+ send unsolicited response and close (RFC4511 suggests unsolicited response
+ is the last PDU coming from the upstream and libldap agrees, so we can't
+ send it for a socket we want to shut down more gracefully)
+- gentle (or very gentle timed out):
+ - set timeout
+ - mark all ops as abandoned
+ - send unbind to all upstreams
+ - send unsolicited to all clients
+- imminent (or gentle timed out):
+ - async close all connections?
+ - exit()
+
+RootDSE:
+- default option is not to care and if a control/exop has special restrictions,
+ it is the admin's job to flag it as such in the load-balancer's config
+- another is not to care about the search request but check each search entry
+ being passed back, check DN and if it's a rootDSE, filter the list of
+ controls/exops/sasl mechs (external!) that are supported
+- last one is to check all search requests for the DN/scope and synthesise the
+ response locally - probably not (would need to configure the complete list of
+ controls, exops, sasl mechs, naming contexts in the balancer)
+
+Potential red flags:
+- we suspend upstreams, if we ever suspend clients we need to be sure we can't
+ create dependency cycles
+ - is this an issue when only suspending the read side of each? Because even if
+ we stop reading from everything, we should eventually flush data to those we
+ can still talk to, as upstreams are flushed, we can start sending new
+ requests from live clients (those that are suspended are due to their own
+ inability to accept data)
+ - we might need to suspend a client if there is a reason to choose a
+ particular upstream (multi-request operation - bind, VC, PR, TXN, ...)
+ - a SASL bind, but that means there are no outstanding ops to receive
+ it holds that !suspended(client) \or !suspended(upstream), so they
+ cannot participate in a cycle
+ - VC - multiple binds at the same time - !!! more analysis needed
+ - PR - should only be able to have one per connection (that's a problem
+ for later, maybe even needs a dedicated upstream connection)
+ - TXN - ??? probably same situation as PR
+ - or if we have a queue for pending Bers on the server, we not need to suspend
+ clients, upstream is only chosen if the queue is free or there is a reason
+ to send it to that particular upstream (multi-stage bind/VC, PR, ...), but
+ that still makes it possible for a client to exhaust all our memory by
+ sending requests (VC or other ones bound to a slow upstream or by not
+ reading the responses at all)
/* dn of the default "monitor" subentry */
#define SLAPD_MONITOR_DN "cn=Monitor"
+/*
+ * LLOADD DEFINITIONS
+ */
+#define LLOADD_DEFAULT_CONFIGFILE LDAP_SYSCONFDIR LDAP_DIRSEP "lloadd.conf"
+
#endif /* _LDAP_CONFIG_H */
## top-level directory of the distribution or, alternatively, at
## <http://www.OpenLDAP.org/license.html>.
-SUBDIRS= slapd
+SUBDIRS= slapd lloadd
--- /dev/null
+# Makefile.in for Load Balancer
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2020 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+PROGRAMS = lloadd
+XPROGRAMS = slloadd
+
+XSRCS = version.c
+
+NT_SRCS = nt_svc.c
+NT_OBJS = nt_svc.o ../../libraries/liblutil/slapdmsg.res
+
+SRCS = main.c globals.c config.c connection.c client.c daemon.c \
+ ch_malloc.c init.c user.c sl_malloc.c value.c \
+ libevent_support.c \
+ $(@PLAT@_SRCS)
+
+OBJS = $(patsubst %.c,%.o,$(SRCS)) $(@PLAT@_OBJS)
+
+LDAP_INCDIR= ../../include -I$(srcdir)
+LDAP_LIBDIR= ../../libraries
+
+BUILD_OPT = "--enable-balancer"
+BUILD_SRV = @BUILD_BALANCER@
+
+all-local-srv: $(PROGRAMS)
+
+# $(LTHREAD_LIBS) must be last!
+XLIBS = $(LLOADD_L)
+XXLIBS = $(LLOADD_LIBS) $(SECURITY_LIBS) $(LUTIL_LIBS)
+XXXLIBS = $(LTHREAD_LIBS)
+
+NT_DEPENDS = slapd.exp
+NT_OBJECTS = slapd.exp symdummy.o $(LLOADD_OBJS) version.o
+
+UNIX_DEPENDS = version.o $(LLOADD_L)
+UNIX_OBJECTS = $(OBJS) version.o
+
+LLOADD_DEPENDS = $(@PLAT@_DEPENDS)
+LLOADD_OBJECTS = $(@PLAT@_OBJECTS)
+
+lloadd: $(LLOADD_DEPENDS) version.o
+ $(LTLINK) -o $@ $(OBJS) version.o $(LIBS)
+
+slloadd: version.o
+ $(LTLINK) -static -o $@ $(OBJS) version.o $(LIBS)
+
+version.c: Makefile
+ @-$(RM) $@
+ $(MKVERSION) -s -n Versionstr lloadd > $@
+
+version.o: version.c $(OBJS) $(LLOADD_L)
+
+install-local-srv: FORCE
+ -$(MKDIR) $(DESTDIR)$(libexecdir)
+ @-$(INSTALL) -m 700 -d $(DESTDIR)$(localstatedir)/openldap-lloadd
+ @( \
+ for prg in $(PROGRAMS); do \
+ $(LTINSTALL) $(INSTALLFLAGS) $(STRIP) -m 755 $$prg$(EXEEXT) \
+ $(DESTDIR)$(libexecdir); \
+ done \
+ )
+
--- /dev/null
+../slapd/ch_malloc.c
\ No newline at end of file
--- /dev/null
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2020 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <ac/socket.h>
+#include <ac/errno.h>
+#include <ac/string.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+
+#include "lutil.h"
+#include "slap.h"
+
+static void client_destroy( Connection *c );
+
+static void
+client_read_cb( evutil_socket_t s, short what, void *arg )
+{
+ Connection *c = arg;
+ Debug( LDAP_DEBUG_CONNS, "client_read_cb: "
+ "connection %lu ready to read\n",
+ c->c_connid );
+ evutil_closesocket( s );
+ client_destroy( c );
+}
+
+static void
+client_write_cb( evutil_socket_t s, short what, void *arg )
+{
+ Connection *c = arg;
+}
+
+Connection *
+client_init(
+ ber_socket_t s,
+ Listener *listener,
+ const char *peername,
+ struct event_base *base,
+ int flags )
+{
+ Connection *c;
+ struct event *event;
+
+ assert( listener != NULL );
+
+ c = connection_init( s, peername, flags );
+
+ event = event_new( base, s, EV_READ|EV_PERSIST, client_read_cb, c );
+ if ( !event ) {
+ Debug( LDAP_DEBUG_ANY, "Read event could not be allocated\n" );
+ goto fail;
+ }
+ event_add( event, NULL );
+ c->c_read_event = event;
+
+ event = event_new( base, s, EV_WRITE, client_write_cb, c );
+ if ( !event ) {
+ Debug( LDAP_DEBUG_ANY, "Write event could not be allocated\n" );
+ goto fail;
+ }
+ /* We only register the write event when we have data pending */
+ c->c_write_event = event;
+
+ c->c_private = listener;
+
+ return c;
+fail:
+ if ( c->c_write_event ) {
+ event_del( c->c_write_event );
+ event_free( c->c_write_event );
+ }
+ if ( c->c_read_event ) {
+ event_del( c->c_read_event );
+ event_free( c->c_read_event );
+ }
+ connection_destroy( c );
+ return NULL;
+}
+
+static void
+client_destroy( Connection *c )
+{
+ event_del( c->c_read_event );
+ event_free( c->c_read_event );
+
+ event_del( c->c_write_event );
+ event_free( c->c_write_event );
+
+ connection_destroy( c );
+}
--- /dev/null
+/* config.c - configuration file handling routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2020 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/ctype.h>
+#include <ac/signal.h>
+#include <ac/socket.h>
+#include <ac/errno.h>
+#include <ac/unistd.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifndef S_ISREG
+#define S_ISREG(m) ( ((m) & _S_IFMT ) == _S_IFREG )
+#endif
+
+#include "slap.h"
+#include "lutil.h"
+#include "lutil_ldap.h"
+#include "config.h"
+
+#ifdef _WIN32
+#define LUTIL_ATOULX lutil_atoullx
+#define Z "I"
+#else
+#define LUTIL_ATOULX lutil_atoulx
+#define Z "z"
+#endif
+
+#define ARGS_STEP 512
+
+/*
+ * defaults for various global variables
+ */
+slap_mask_t global_allows = 0;
+slap_mask_t global_disallows = 0;
+int global_gentlehup = 0;
+int global_idletimeout = 0;
+char *global_host = NULL;
+
+static FILE *logfile;
+static char *logfileName;
+
+ber_len_t sockbuf_max_incoming = SLAP_SB_MAX_INCOMING_DEFAULT;
+ber_len_t sockbuf_max_incoming_auth = SLAP_SB_MAX_INCOMING_AUTH;
+
+char *slapd_pid_file = NULL;
+char *slapd_args_file = NULL;
+
+static int fp_getline( FILE *fp, ConfigArgs *c );
+static void fp_getline_init( ConfigArgs *c );
+
+static char *strtok_quote(
+ char *line,
+ char *sep,
+ char **quote_ptr,
+ int *inquote );
+
+typedef struct ConfigFile {
+ struct ConfigFile *c_sibs;
+ struct ConfigFile *c_kids;
+ struct berval c_file;
+ BerVarray c_dseFiles;
+} ConfigFile;
+
+static ConfigFile *cfn;
+
+static ConfigDriver config_fname;
+static ConfigDriver config_generic;
+#ifdef LDAP_TCP_BUFFER
+static ConfigDriver config_tcp_buffer;
+#endif /* LDAP_TCP_BUFFER */
+static ConfigDriver config_restrict;
+static ConfigDriver config_loglevel;
+static ConfigDriver config_include;
+#ifdef HAVE_TLS
+static ConfigDriver config_tls_option;
+static ConfigDriver config_tls_config;
+#endif
+
+enum {
+ CFG_ACL = 1,
+ CFG_BACKEND,
+ CFG_DATABASE,
+ CFG_TLS_RAND,
+ CFG_TLS_CIPHER,
+ CFG_TLS_PROTOCOL_MIN,
+ CFG_TLS_CERT_FILE,
+ CFG_TLS_CERT_KEY,
+ CFG_TLS_CA_PATH,
+ CFG_TLS_CA_FILE,
+ CFG_TLS_DH_FILE,
+ CFG_TLS_VERIFY,
+ CFG_TLS_CRLCHECK,
+ CFG_TLS_CRL_FILE,
+ CFG_CONCUR,
+ CFG_THREADS,
+ CFG_LOGFILE,
+ CFG_MIRRORMODE,
+ CFG_LTHREADS,
+ CFG_THREADQS,
+ CFG_TLS_ECNAME,
+ CFG_TLS_CACERT,
+ CFG_TLS_CERT,
+ CFG_TLS_KEY,
+
+ CFG_LAST
+};
+
+/* alphabetical ordering */
+
+static ConfigTable config_back_cf_table[] = {
+ /* This attr is read-only */
+ { "", "", 0, 0, 0,
+ ARG_MAGIC,
+ &config_fname,
+ },
+ { "argsfile", "file", 2, 2, 0,
+ ARG_STRING,
+ &slapd_args_file,
+ },
+ { "concurrency", "level", 2, 2, 0,
+ ARG_INT|ARG_MAGIC|CFG_CONCUR,
+ &config_generic,
+ },
+ { "database", "type", 2, 2, 0,
+ ARG_MAGIC|CFG_DATABASE,
+ &config_generic,
+ },
+ { "gentlehup", "on|off", 2, 2, 0,
+#ifdef SIGHUP
+ ARG_ON_OFF,
+ &global_gentlehup,
+#else
+ ARG_IGNORED,
+ NULL,
+#endif
+ },
+ { "idletimeout", "timeout", 2, 2, 0,
+ ARG_INT,
+ &global_idletimeout,
+ },
+ { "include", "file", 2, 2, 0,
+ ARG_MAGIC,
+ &config_include,
+ },
+ { "listener-threads", "count", 2, 0, 0,
+ ARG_UINT|ARG_MAGIC|CFG_LTHREADS,
+ &config_generic,
+ },
+ { "logfile", "file", 2, 2, 0,
+ ARG_STRING|ARG_MAGIC|CFG_LOGFILE,
+ &config_generic,
+ },
+ { "loglevel", "level", 2, 0, 0,
+ ARG_MAGIC,
+ &config_loglevel,
+ },
+ { "pidfile", "file", 2, 2, 0,
+ ARG_STRING,
+ &slapd_pid_file,
+ },
+ { "restrict", "op_list", 2, 0, 0,
+ ARG_MAGIC,
+ &config_restrict,
+ },
+ { "sockbuf_max_incoming", "max", 2, 2, 0,
+ ARG_BER_LEN_T,
+ &sockbuf_max_incoming,
+ },
+ { "sockbuf_max_incoming_auth", "max", 2, 2, 0,
+ ARG_BER_LEN_T,
+ &sockbuf_max_incoming_auth,
+ },
+ { "tcp-buffer", "[listener=<listener>] [{read|write}=]size", 0, 0, 0,
+#ifdef LDAP_TCP_BUFFER
+ ARG_MAGIC,
+ &config_tcp_buffer,
+#else
+ ARG_IGNORED,
+ NULL,
+#endif
+ },
+ { "threads", "count", 2, 2, 0,
+ ARG_INT|ARG_MAGIC|CFG_THREADS,
+ &config_generic,
+ },
+ { "threadqueues", "count", 2, 2, 0,
+ ARG_INT|ARG_MAGIC|CFG_THREADQS,
+ &config_generic,
+ },
+ { "TLSCACertificate", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+ CFG_TLS_CACERT|ARG_BINARY|ARG_MAGIC,
+ &config_tls_option,
+#else
+ ARG_IGNORED,
+ NULL,
+#endif
+ },
+ { "TLSCACertificateFile", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+ CFG_TLS_CA_FILE|ARG_STRING|ARG_MAGIC,
+ &config_tls_option,
+#else
+ ARG_IGNORED,
+ NULL,
+#endif
+ },
+ { "TLSCACertificatePath", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+ CFG_TLS_CA_PATH|ARG_STRING|ARG_MAGIC,
+ &config_tls_option,
+#else
+ ARG_IGNORED,
+ NULL,
+#endif
+ },
+ { "TLSCertificate", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+ CFG_TLS_CERT|ARG_BINARY|ARG_MAGIC,
+ &config_tls_option,
+#else
+ ARG_IGNORED,
+ NULL,
+#endif
+ },
+ { "TLSCertificateFile", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+ CFG_TLS_CERT_FILE|ARG_STRING|ARG_MAGIC,
+ &config_tls_option,
+#else
+ ARG_IGNORED,
+ NULL,
+#endif
+ },
+ { "TLSCertificateKey", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+ CFG_TLS_KEY|ARG_BINARY|ARG_MAGIC,
+ &config_tls_option,
+#else
+ ARG_IGNORED,
+ NULL,
+#endif
+ },
+ { "TLSCertificateKeyFile", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+ CFG_TLS_CERT_KEY|ARG_STRING|ARG_MAGIC,
+ &config_tls_option,
+#else
+ ARG_IGNORED,
+ NULL,
+#endif
+ },
+ { "TLSCipherSuite", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+ CFG_TLS_CIPHER|ARG_STRING|ARG_MAGIC,
+ &config_tls_option,
+#else
+ ARG_IGNORED,
+ NULL,
+#endif
+ },
+ { "TLSCRLCheck", NULL, 2, 2, 0,
+#if defined(HAVE_TLS) && defined(HAVE_OPENSSL)
+ CFG_TLS_CRLCHECK|ARG_STRING|ARG_MAGIC,
+ &config_tls_config,
+#else
+ ARG_IGNORED,
+ NULL,
+#endif
+ },
+ { "TLSCRLFile", NULL, 2, 2, 0,
+#if defined(HAVE_GNUTLS)
+ CFG_TLS_CRL_FILE|ARG_STRING|ARG_MAGIC,
+ &config_tls_option,
+#else
+ ARG_IGNORED,
+ NULL,
+#endif
+ },
+ { "TLSRandFile", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+ CFG_TLS_RAND|ARG_STRING|ARG_MAGIC,
+ &config_tls_option,
+#else
+ ARG_IGNORED,
+ NULL,
+#endif
+ },
+ { "TLSVerifyClient", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+ CFG_TLS_VERIFY|ARG_STRING|ARG_MAGIC,
+ &config_tls_config,
+#else
+ ARG_IGNORED,
+ NULL,
+#endif
+ },
+ { "TLSDHParamFile", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+ CFG_TLS_DH_FILE|ARG_STRING|ARG_MAGIC,
+ &config_tls_option,
+#else
+ ARG_IGNORED,
+ NULL,
+#endif
+ },
+ { "TLSECName", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+ CFG_TLS_ECNAME|ARG_STRING|ARG_MAGIC,
+ &config_tls_option,
+#else
+ ARG_IGNORED,
+ NULL,
+#endif
+ },
+ { "TLSProtocolMin", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+ CFG_TLS_PROTOCOL_MIN|ARG_STRING|ARG_MAGIC,
+ &config_tls_config,
+#else
+ ARG_IGNORED,
+ NULL,
+#endif
+ },
+
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED, NULL }
+};
+
+static int
+config_generic( ConfigArgs *c )
+{
+ switch ( c->type ) {
+ case CFG_CONCUR:
+ ldap_pvt_thread_set_concurrency( c->value_int );
+ break;
+
+ case CFG_THREADS:
+ if ( c->value_int < 2 ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "threads=%d smaller than minimum value 2",
+ c->value_int );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+
+ } else if ( c->value_int > 2 * SLAP_MAX_WORKER_THREADS ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "warning, threads=%d larger than twice the default "
+ "(2*%d=%d); YMMV",
+ c->value_int, SLAP_MAX_WORKER_THREADS,
+ 2 * SLAP_MAX_WORKER_THREADS );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ }
+ if ( slapMode & SLAP_SERVER_MODE )
+ ldap_pvt_thread_pool_maxthreads(
+ &connection_pool, c->value_int );
+ connection_pool_max = c->value_int; /* save for reference */
+ break;
+
+ case CFG_THREADQS:
+ if ( c->value_int < 1 ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "threadqueues=%d smaller than minimum value 1",
+ c->value_int );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ if ( slapMode & SLAP_SERVER_MODE )
+ ldap_pvt_thread_pool_queues( &connection_pool, c->value_int );
+ connection_pool_queues = c->value_int; /* save for reference */
+ break;
+
+ case CFG_LTHREADS: {
+ int mask = 0;
+ /* use a power of two */
+ while ( c->value_uint > 1 ) {
+ c->value_uint >>= 1;
+ mask <<= 1;
+ mask |= 1;
+ }
+ slapd_daemon_mask = mask;
+ slapd_daemon_threads = mask + 1;
+ } break;
+
+ case CFG_LOGFILE: {
+ if ( logfileName ) ch_free( logfileName );
+ logfileName = c->value_string;
+ logfile = fopen( logfileName, "w" );
+ if ( logfile ) lutil_debug_file( logfile );
+ } break;
+
+ default:
+ Debug( LDAP_DEBUG_ANY, "%s: unknown CFG_TYPE %d.\n",
+ c->log, c->type );
+ return 1;
+ }
+ return 0;
+}
+
+static int
+config_fname( ConfigArgs *c )
+{
+ return 0;
+}
+
+/*
+ * [listener=<listener>] [{read|write}=]<size>
+ */
+
+#ifdef LDAP_TCP_BUFFER
+static BerVarray tcp_buffer;
+int tcp_buffer_num;
+
+#define SLAP_TCP_RMEM ( 0x1U )
+#define SLAP_TCP_WMEM ( 0x2U )
+
+static int
+tcp_buffer_parse(
+ struct berval *val,
+ int argc,
+ char **argv,
+ int *size,
+ int *rw,
+ Listener **l )
+{
+ int i, rc = LDAP_SUCCESS;
+ LDAPURLDesc *lud = NULL;
+ char *ptr;
+
+ if ( val != NULL && argv == NULL ) {
+ char *s = val->bv_val;
+
+ argv = ldap_str2charray( s, " \t" );
+ if ( argv == NULL ) {
+ return LDAP_OTHER;
+ }
+ }
+
+ i = 0;
+ if ( strncasecmp( argv[i], "listener=", STRLENOF("listener=") ) == 0 ) {
+ char *url = argv[i] + STRLENOF("listener=");
+
+ if ( ldap_url_parse( url, &lud ) ) {
+ rc = LDAP_INVALID_SYNTAX;
+ goto done;
+ }
+
+ *l = config_check_my_url( url, lud );
+ if ( *l == NULL ) {
+ rc = LDAP_NO_SUCH_ATTRIBUTE;
+ goto done;
+ }
+
+ i++;
+ }
+
+ ptr = argv[i];
+ if ( strncasecmp( ptr, "read=", STRLENOF("read=") ) == 0 ) {
+ *rw |= SLAP_TCP_RMEM;
+ ptr += STRLENOF("read=");
+
+ } else if ( strncasecmp( ptr, "write=", STRLENOF("write=") ) == 0 ) {
+ *rw |= SLAP_TCP_WMEM;
+ ptr += STRLENOF("write=");
+
+ } else {
+ *rw |= ( SLAP_TCP_RMEM | SLAP_TCP_WMEM );
+ }
+
+ /* accept any base */
+ if ( lutil_atoix( size, ptr, 0 ) ) {
+ rc = LDAP_INVALID_SYNTAX;
+ goto done;
+ }
+
+done:;
+ if ( val != NULL && argv != NULL ) {
+ ldap_charray_free( argv );
+ }
+
+ if ( lud != NULL ) {
+ ldap_free_urldesc( lud );
+ }
+
+ return rc;
+}
+
+static int
+tcp_buffer_unparse( int size, int rw, Listener *l, struct berval *val )
+{
+ char buf[sizeof("2147483648")], *ptr;
+
+ /* unparse for later use */
+ val->bv_len = snprintf( buf, sizeof(buf), "%d", size );
+ if ( l != NULL ) {
+ val->bv_len += STRLENOF( "listener="
+ " " ) +
+ l->sl_url.bv_len;
+ }
+
+ if ( rw != ( SLAP_TCP_RMEM | SLAP_TCP_WMEM ) ) {
+ if ( rw & SLAP_TCP_RMEM ) {
+ val->bv_len += STRLENOF("read=");
+ } else if ( rw & SLAP_TCP_WMEM ) {
+ val->bv_len += STRLENOF("write=");
+ }
+ }
+
+ val->bv_val = SLAP_MALLOC( val->bv_len + 1 );
+
+ ptr = val->bv_val;
+
+ if ( l != NULL ) {
+ ptr = lutil_strcopy( ptr, "listener=" );
+ ptr = lutil_strncopy( ptr, l->sl_url.bv_val, l->sl_url.bv_len );
+ *ptr++ = ' ';
+ }
+
+ if ( rw != ( SLAP_TCP_RMEM | SLAP_TCP_WMEM ) ) {
+ if ( rw & SLAP_TCP_RMEM ) {
+ ptr = lutil_strcopy( ptr, "read=" );
+ } else if ( rw & SLAP_TCP_WMEM ) {
+ ptr = lutil_strcopy( ptr, "write=" );
+ }
+ }
+
+ ptr = lutil_strcopy( ptr, buf );
+ *ptr = '\0';
+
+ assert( val->bv_val + val->bv_len == ptr );
+
+ return LDAP_SUCCESS;
+}
+
+static int
+tcp_buffer_add_one( int argc, char **argv )
+{
+ int rc = 0;
+ int size = -1, rw = 0;
+ Listener *l = NULL;
+
+ struct berval val;
+
+ /* parse */
+ rc = tcp_buffer_parse( NULL, argc, argv, &size, &rw, &l );
+ if ( rc != 0 ) {
+ return rc;
+ }
+
+ /* unparse for later use */
+ rc = tcp_buffer_unparse( size, rw, l, &val );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ /* use parsed values */
+ if ( l != NULL ) {
+ int i;
+ Listener **ll = slapd_get_listeners();
+
+ for ( i = 0; ll[i] != NULL; i++ ) {
+ if ( ll[i] == l ) break;
+ }
+
+ if ( ll[i] == NULL ) {
+ return LDAP_NO_SUCH_ATTRIBUTE;
+ }
+
+ /* buffer only applies to TCP listeners;
+ * we do not do any check here, and delegate them
+ * to setsockopt(2) */
+ if ( rw & SLAP_TCP_RMEM ) l->sl_tcp_rmem = size;
+ if ( rw & SLAP_TCP_WMEM ) l->sl_tcp_wmem = size;
+
+ for ( i++; ll[i] != NULL && bvmatch( &l->sl_url, &ll[i]->sl_url );
+ i++ ) {
+ if ( rw & SLAP_TCP_RMEM ) ll[i]->sl_tcp_rmem = size;
+ if ( rw & SLAP_TCP_WMEM ) ll[i]->sl_tcp_wmem = size;
+ }
+
+ } else {
+ /* NOTE: this affects listeners without a specific setting,
+ * does not set all listeners */
+ if ( rw & SLAP_TCP_RMEM ) slapd_tcp_rmem = size;
+ if ( rw & SLAP_TCP_WMEM ) slapd_tcp_wmem = size;
+ }
+
+ tcp_buffer = SLAP_REALLOC(
+ tcp_buffer, sizeof(struct berval) * ( tcp_buffer_num + 2 ) );
+ /* append */
+ tcp_buffer[tcp_buffer_num] = val;
+
+ tcp_buffer_num++;
+ BER_BVZERO( &tcp_buffer[tcp_buffer_num] );
+
+ return rc;
+}
+
+static int
+config_tcp_buffer( ConfigArgs *c )
+{
+ int rc;
+
+ rc = tcp_buffer_add_one( c->argc - 1, &c->argv[1] );
+ if ( rc ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg), "<%s> unable to add value #%d",
+ c->argv[0], tcp_buffer_num );
+ Debug( LDAP_DEBUG_ANY, "%s: %s\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ return 0;
+}
+#endif /* LDAP_TCP_BUFFER */
+
+static int
+config_restrict( ConfigArgs *c )
+{
+ slap_mask_t restrictops = 0;
+ int i;
+ slap_verbmasks restrictable_ops[] = {
+ { BER_BVC("bind"), SLAP_RESTRICT_OP_BIND },
+ { BER_BVC("add"), SLAP_RESTRICT_OP_ADD },
+ { BER_BVC("modify"), SLAP_RESTRICT_OP_MODIFY },
+ { BER_BVC("rename"), SLAP_RESTRICT_OP_RENAME },
+ { BER_BVC("modrdn"), 0 },
+ { BER_BVC("delete"), SLAP_RESTRICT_OP_DELETE },
+ { BER_BVC("search"), SLAP_RESTRICT_OP_SEARCH },
+ { BER_BVC("compare"), SLAP_RESTRICT_OP_COMPARE },
+ { BER_BVC("read"), SLAP_RESTRICT_OP_READS },
+ { BER_BVC("write"), SLAP_RESTRICT_OP_WRITES },
+ { BER_BVC("extended"), SLAP_RESTRICT_OP_EXTENDED },
+ { BER_BVC("extended=" LDAP_EXOP_START_TLS), SLAP_RESTRICT_EXOP_START_TLS },
+ { BER_BVC("extended=" LDAP_EXOP_MODIFY_PASSWD), SLAP_RESTRICT_EXOP_MODIFY_PASSWD },
+ { BER_BVC("extended=" LDAP_EXOP_X_WHO_AM_I), SLAP_RESTRICT_EXOP_WHOAMI },
+ { BER_BVC("extended=" LDAP_EXOP_X_CANCEL), SLAP_RESTRICT_EXOP_CANCEL },
+ { BER_BVC("all"), SLAP_RESTRICT_OP_ALL },
+ { BER_BVNULL, 0 }
+ };
+
+ i = verbs_to_mask( c->argc, c->argv, restrictable_ops, &restrictops );
+ if ( i ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg), "<%s> unknown operation",
+ c->argv[0] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s %s\n",
+ c->log, c->cr_msg, c->argv[i] );
+ return 1;
+ }
+ if ( restrictops & SLAP_RESTRICT_OP_EXTENDED )
+ restrictops &= ~SLAP_RESTRICT_EXOP_MASK;
+ return 0;
+}
+
+static slap_verbmasks *loglevel_ops;
+
+static int
+loglevel_init( void )
+{
+ slap_verbmasks lo[] = {
+ { BER_BVC("Any"), (slap_mask_t)LDAP_DEBUG_ANY },
+ { BER_BVC("Trace"), LDAP_DEBUG_TRACE },
+ { BER_BVC("Packets"), LDAP_DEBUG_PACKETS },
+ { BER_BVC("Args"), LDAP_DEBUG_ARGS },
+ { BER_BVC("Conns"), LDAP_DEBUG_CONNS },
+ { BER_BVC("BER"), LDAP_DEBUG_BER },
+ { BER_BVC("Filter"), LDAP_DEBUG_FILTER },
+ { BER_BVC("Config"), LDAP_DEBUG_CONFIG },
+ { BER_BVC("ACL"), LDAP_DEBUG_ACL },
+ { BER_BVC("Stats"), LDAP_DEBUG_STATS },
+ { BER_BVC("Stats2"), LDAP_DEBUG_STATS2 },
+ { BER_BVC("Shell"), LDAP_DEBUG_SHELL },
+ { BER_BVC("Parse"), LDAP_DEBUG_PARSE },
+ { BER_BVC("Sync"), LDAP_DEBUG_SYNC },
+ { BER_BVC("None"), LDAP_DEBUG_NONE },
+ { BER_BVNULL, 0 }
+ };
+
+ return slap_verbmasks_init( &loglevel_ops, lo );
+}
+
+static void
+loglevel_destroy( void )
+{
+ if ( loglevel_ops ) {
+ (void)slap_verbmasks_destroy( loglevel_ops );
+ }
+ loglevel_ops = NULL;
+}
+
+int
+str2loglevel( const char *s, int *l )
+{
+ int i;
+
+ if ( loglevel_ops == NULL ) {
+ loglevel_init();
+ }
+
+ i = verb_to_mask( s, loglevel_ops );
+
+ if ( BER_BVISNULL( &loglevel_ops[i].word ) ) {
+ return -1;
+ }
+
+ *l = loglevel_ops[i].mask;
+
+ return 0;
+}
+
+int
+loglevel2bvarray( int l, BerVarray *bva )
+{
+ if ( loglevel_ops == NULL ) {
+ loglevel_init();
+ }
+
+ if ( l == 0 ) {
+ struct berval bv = BER_BVC("0");
+ return value_add_one( bva, &bv );
+ }
+
+ return mask_to_verbs( loglevel_ops, l, bva );
+}
+
+int
+loglevel_print( FILE *out )
+{
+ int i;
+
+ if ( loglevel_ops == NULL ) {
+ loglevel_init();
+ }
+
+ fprintf( out, "Installed log subsystems:\n\n" );
+ for ( i = 0; !BER_BVISNULL( &loglevel_ops[i].word ); i++ ) {
+ unsigned mask = loglevel_ops[i].mask & 0xffffffffUL;
+ fprintf( out,
+ ( mask == ( (slap_mask_t)-1 & 0xffffffffUL ) ?
+ "\t%-30s (-1, 0xffffffff)\n" :
+ "\t%-30s (%u, 0x%x)\n" ),
+ loglevel_ops[i].word.bv_val, mask, mask );
+ }
+
+ fprintf( out,
+ "\nNOTE: custom log subsystems may be later installed "
+ "by specific code\n\n" );
+
+ return 0;
+}
+
+static int config_syslog;
+
+static int
+config_loglevel( ConfigArgs *c )
+{
+ int i;
+
+ if ( loglevel_ops == NULL ) {
+ loglevel_init();
+ }
+
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ /* Get default or commandline slapd setting */
+ if ( ldap_syslog && !config_syslog ) config_syslog = ldap_syslog;
+ return loglevel2bvarray( config_syslog, &c->rvalue_vals );
+
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ if ( !c->line ) {
+ config_syslog = 0;
+ } else {
+ i = verb_to_mask( c->line, loglevel_ops );
+ config_syslog &= ~loglevel_ops[i].mask;
+ }
+ if ( slapMode & SLAP_SERVER_MODE ) {
+ ldap_syslog = config_syslog;
+ }
+ return 0;
+ }
+
+ for ( i = 1; i < c->argc; i++ ) {
+ int level;
+
+ if ( isdigit( (unsigned char)c->argv[i][0] ) || c->argv[i][0] == '-' ) {
+ if ( lutil_atoix( &level, c->argv[i], 0 ) != 0 ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "<%s> unable to parse level",
+ c->argv[0] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s \"%s\"\n",
+ c->log, c->cr_msg, c->argv[i] );
+ return 1;
+ }
+ } else {
+ if ( str2loglevel( c->argv[i], &level ) ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg), "<%s> unknown level",
+ c->argv[0] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s \"%s\"\n",
+ c->log, c->cr_msg, c->argv[i] );
+ return 1;
+ }
+ }
+ /* Explicitly setting a zero clears all the levels */
+ if ( level )
+ config_syslog |= level;
+ else
+ config_syslog = 0;
+ }
+ if ( slapMode & SLAP_SERVER_MODE ) {
+ ldap_syslog = config_syslog;
+ }
+ return 0;
+}
+
+static int
+config_include( ConfigArgs *c )
+{
+ int savelineno = c->lineno;
+ int rc;
+ ConfigFile *cf;
+ ConfigFile *cfsave = cfn;
+ ConfigFile *cf2 = NULL;
+
+ /* Leftover from RE23. No dynamic config for include files */
+ if ( c->op == SLAP_CONFIG_EMIT || c->op == LDAP_MOD_DELETE ) return 1;
+
+ cf = ch_calloc( 1, sizeof(ConfigFile) );
+ if ( cfn->c_kids ) {
+ for ( cf2 = cfn->c_kids; cf2 && cf2->c_sibs; cf2 = cf2->c_sibs )
+ /* empty */;
+ cf2->c_sibs = cf;
+ } else {
+ cfn->c_kids = cf;
+ }
+ cfn = cf;
+ ber_str2bv( c->argv[1], 0, 1, &cf->c_file );
+ rc = read_config_file( c->argv[1], c->depth + 1, c, config_back_cf_table );
+ c->lineno = savelineno - 1;
+ cfn = cfsave;
+ if ( rc ) {
+ if ( cf2 )
+ cf2->c_sibs = NULL;
+ else
+ cfn->c_kids = NULL;
+ ch_free( cf->c_file.bv_val );
+ ch_free( cf );
+ } else {
+ c->ca_private = cf;
+ }
+ return rc;
+}
+
+#ifdef HAVE_TLS
+static int
+config_tls_cleanup( ConfigArgs *c )
+{
+ int rc = 0;
+
+ if ( slap_tls_ld ) {
+ int opt = 1;
+
+ ldap_pvt_tls_ctx_free( slap_tls_ctx );
+ slap_tls_ctx = NULL;
+
+ /* Force new ctx to be created */
+ rc = ldap_pvt_tls_set_option(
+ slap_tls_ld, LDAP_OPT_X_TLS_NEWCTX, &opt );
+ if ( rc == 0 ) {
+ /* The ctx's refcount is bumped up here */
+ ldap_pvt_tls_get_option(
+ slap_tls_ld, LDAP_OPT_X_TLS_CTX, &slap_tls_ctx );
+ } else {
+ if ( rc == LDAP_NOT_SUPPORTED )
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ else
+ rc = LDAP_OTHER;
+ }
+ }
+ return rc;
+}
+
+static int
+config_tls_option( ConfigArgs *c )
+{
+ int flag, rc;
+ int berval = 0;
+ LDAP *ld = slap_tls_ld;
+ switch ( c->type ) {
+ case CFG_TLS_RAND:
+ flag = LDAP_OPT_X_TLS_RANDOM_FILE;
+ ld = NULL;
+ break;
+ case CFG_TLS_CIPHER:
+ flag = LDAP_OPT_X_TLS_CIPHER_SUITE;
+ break;
+ case CFG_TLS_CERT_FILE:
+ flag = LDAP_OPT_X_TLS_CERTFILE;
+ break;
+ case CFG_TLS_CERT_KEY:
+ flag = LDAP_OPT_X_TLS_KEYFILE;
+ break;
+ case CFG_TLS_CA_PATH:
+ flag = LDAP_OPT_X_TLS_CACERTDIR;
+ break;
+ case CFG_TLS_CA_FILE:
+ flag = LDAP_OPT_X_TLS_CACERTFILE;
+ break;
+ case CFG_TLS_DH_FILE:
+ flag = LDAP_OPT_X_TLS_DHFILE;
+ break;
+ case CFG_TLS_ECNAME:
+ flag = LDAP_OPT_X_TLS_ECNAME;
+ break;
+#ifdef HAVE_GNUTLS
+ case CFG_TLS_CRL_FILE:
+ flag = LDAP_OPT_X_TLS_CRLFILE;
+ break;
+#endif
+ case CFG_TLS_CACERT:
+ flag = LDAP_OPT_X_TLS_CACERT;
+ berval = 1;
+ break;
+ case CFG_TLS_CERT:
+ flag = LDAP_OPT_X_TLS_CERT;
+ berval = 1;
+ break;
+ case CFG_TLS_KEY:
+ flag = LDAP_OPT_X_TLS_KEY;
+ berval = 1;
+ break;
+ default:
+ Debug( LDAP_DEBUG_ANY, "%s: "
+ "unknown tls_option <0x%x>\n",
+ c->log, c->type );
+ return 1;
+ }
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ return ldap_pvt_tls_get_option( ld, flag,
+ berval ? (void *)&c->value_bv : (void *)&c->value_string );
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ config_push_cleanup( c, config_tls_cleanup );
+ return ldap_pvt_tls_set_option( ld, flag, NULL );
+ }
+ if ( !berval ) ch_free( c->value_string );
+ config_push_cleanup( c, config_tls_cleanup );
+ rc = ldap_pvt_tls_set_option(
+ ld, flag, berval ? (void *)&c->value_bv : (void *)c->argv[1] );
+ if ( berval ) ch_free( c->value_bv.bv_val );
+ return rc;
+}
+
+/* FIXME: this ought to be provided by libldap */
+static int
+config_tls_config( ConfigArgs *c )
+{
+ int i, flag;
+ switch ( c->type ) {
+ case CFG_TLS_CRLCHECK:
+ flag = LDAP_OPT_X_TLS_CRLCHECK;
+ break;
+ case CFG_TLS_VERIFY:
+ flag = LDAP_OPT_X_TLS_REQUIRE_CERT;
+ break;
+ case CFG_TLS_PROTOCOL_MIN:
+ flag = LDAP_OPT_X_TLS_PROTOCOL_MIN;
+ break;
+ default:
+ Debug( LDAP_DEBUG_ANY, "%s: "
+ "unknown tls_option <0x%x>\n",
+ c->log, c->type );
+ return 1;
+ }
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ return slap_tls_get_config( slap_tls_ld, flag, &c->value_string );
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ int i = 0;
+ config_push_cleanup( c, config_tls_cleanup );
+ return ldap_pvt_tls_set_option( slap_tls_ld, flag, &i );
+ }
+ ch_free( c->value_string );
+ config_push_cleanup( c, config_tls_cleanup );
+ if ( isdigit( (unsigned char)c->argv[1][0] ) &&
+ c->type != CFG_TLS_PROTOCOL_MIN ) {
+ if ( lutil_atoi( &i, c->argv[1] ) != 0 ) {
+ Debug( LDAP_DEBUG_ANY, "%s: "
+ "unable to parse %s \"%s\"\n",
+ c->log, c->argv[0], c->argv[1] );
+ return 1;
+ }
+ return ldap_pvt_tls_set_option( slap_tls_ld, flag, &i );
+ } else {
+ return ldap_pvt_tls_config( slap_tls_ld, flag, c->argv[1] );
+ }
+}
+#endif
+
+void
+init_config_argv( ConfigArgs *c )
+{
+ c->argv = ch_calloc( ARGS_STEP + 1, sizeof(*c->argv) );
+ c->argv_size = ARGS_STEP + 1;
+}
+
+ConfigTable *
+config_find_keyword( ConfigTable *Conf, ConfigArgs *c )
+{
+ int i;
+
+ for ( i = 0; Conf[i].name; i++ )
+ if ( ( Conf[i].length &&
+ ( !strncasecmp(
+ c->argv[0], Conf[i].name, Conf[i].length ) ) ) ||
+ ( !strcasecmp( c->argv[0], Conf[i].name ) ) )
+ break;
+ if ( !Conf[i].name ) return NULL;
+ if ( (Conf[i].arg_type & ARGS_TYPES) == ARG_BINARY ) {
+ size_t decode_len = LUTIL_BASE64_DECODE_LEN( c->linelen );
+ ch_free( c->tline );
+ c->tline = ch_malloc( decode_len + 1 );
+ c->linelen = lutil_b64_pton( c->line, c->tline, decode_len );
+ if ( c->linelen < 0 ) {
+ ch_free( c->tline );
+ c->tline = NULL;
+ return NULL;
+ }
+ c->line = c->tline;
+ }
+ return Conf + i;
+}
+
+int
+config_check_vals( ConfigTable *Conf, ConfigArgs *c, int check_only )
+{
+ int arg_user, arg_type, arg_syn, iarg;
+ unsigned uiarg;
+ long larg;
+ unsigned long ularg;
+ ber_len_t barg;
+
+ if ( Conf->arg_type == ARG_IGNORED ) {
+ Debug( LDAP_DEBUG_CONFIG, "%s: keyword <%s> ignored\n",
+ c->log, Conf->name );
+ return 0;
+ }
+ arg_type = Conf->arg_type & ARGS_TYPES;
+ arg_user = Conf->arg_type & ARGS_USERLAND;
+ arg_syn = Conf->arg_type & ARGS_SYNTAX;
+
+ if ( Conf->min_args && ( c->argc < Conf->min_args ) ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg), "<%s> missing <%s> argument",
+ c->argv[0], Conf->what ? Conf->what : "" );
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "%s: keyword %s\n",
+ c->log, c->cr_msg );
+ return ARG_BAD_CONF;
+ }
+ if ( Conf->max_args && ( c->argc > Conf->max_args ) ) {
+ char *ignored = " ignored";
+
+ snprintf( c->cr_msg, sizeof(c->cr_msg), "<%s> extra cruft after <%s>",
+ c->argv[0], Conf->what );
+
+ ignored = "";
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "%s: %s%s.\n",
+ c->log, c->cr_msg, ignored );
+ return ARG_BAD_CONF;
+ }
+ if ( (arg_syn & ARG_PAREN) && *c->argv[1] != '(' /*')'*/ ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg), "<%s> old format not supported",
+ c->argv[0] );
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "%s: %s\n",
+ c->log, c->cr_msg );
+ return ARG_BAD_CONF;
+ }
+ if ( arg_type && !Conf->arg_item && !(arg_syn & ARG_OFFSET) ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "<%s> invalid config_table, arg_item is NULL",
+ c->argv[0] );
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "%s: %s\n",
+ c->log, c->cr_msg );
+ return ARG_BAD_CONF;
+ }
+ c->type = arg_user;
+ memset( &c->values, 0, sizeof(c->values) );
+ if ( arg_type == ARG_STRING ) {
+ assert( c->argc == 2 );
+ if ( !check_only ) c->value_string = ch_strdup( c->argv[1] );
+ } else if ( arg_type == ARG_BERVAL ) {
+ assert( c->argc == 2 );
+ if ( !check_only ) ber_str2bv( c->argv[1], 0, 1, &c->value_bv );
+ } else if ( arg_type == ARG_BINARY ) {
+ assert( c->argc == 2 );
+ if ( !check_only ) {
+ c->value_bv.bv_len = c->linelen;
+ c->value_bv.bv_val = ch_malloc( c->linelen );
+ AC_MEMCPY( c->value_bv.bv_val, c->line, c->linelen );
+ }
+ } else { /* all numeric */
+ int j;
+ iarg = 0;
+ larg = 0;
+ barg = 0;
+ switch ( arg_type ) {
+ case ARG_INT:
+ assert( c->argc == 2 );
+ if ( lutil_atoix( &iarg, c->argv[1], 0 ) != 0 ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "<%s> unable to parse \"%s\" as int",
+ c->argv[0], c->argv[1] );
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "%s: %s\n",
+ c->log, c->cr_msg );
+ return ARG_BAD_CONF;
+ }
+ break;
+ case ARG_UINT:
+ assert( c->argc == 2 );
+ if ( lutil_atoux( &uiarg, c->argv[1], 0 ) != 0 ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "<%s> unable to parse \"%s\" as unsigned int",
+ c->argv[0], c->argv[1] );
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "%s: %s\n",
+ c->log, c->cr_msg );
+ return ARG_BAD_CONF;
+ }
+ break;
+ case ARG_LONG:
+ assert( c->argc == 2 );
+ if ( lutil_atolx( &larg, c->argv[1], 0 ) != 0 ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "<%s> unable to parse \"%s\" as long",
+ c->argv[0], c->argv[1] );
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "%s: %s\n",
+ c->log, c->cr_msg );
+ return ARG_BAD_CONF;
+ }
+ break;
+ case ARG_ULONG:
+ assert( c->argc == 2 );
+ if ( LUTIL_ATOULX( &ularg, c->argv[1], 0 ) != 0 ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "<%s> unable to parse \"%s\" as unsigned long",
+ c->argv[0], c->argv[1] );
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "%s: %s\n",
+ c->log, c->cr_msg );
+ return ARG_BAD_CONF;
+ }
+ break;
+ case ARG_BER_LEN_T: {
+ unsigned long l;
+ assert( c->argc == 2 );
+ if ( lutil_atoulx( &l, c->argv[1], 0 ) != 0 ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "<%s> unable to parse \"%s\" as ber_len_t",
+ c->argv[0], c->argv[1] );
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "%s: %s\n",
+ c->log, c->cr_msg );
+ return ARG_BAD_CONF;
+ }
+ barg = (ber_len_t)l;
+ } break;
+ case ARG_ON_OFF:
+ /* note: this is an explicit exception
+ * to the "need exactly 2 args" rule */
+ if ( c->argc == 1 ) {
+ iarg = 1;
+ } else if ( !strcasecmp( c->argv[1], "on" ) ||
+ !strcasecmp( c->argv[1], "true" ) ||
+ !strcasecmp( c->argv[1], "yes" ) ) {
+ iarg = 1;
+ } else if ( !strcasecmp( c->argv[1], "off" ) ||
+ !strcasecmp( c->argv[1], "false" ) ||
+ !strcasecmp( c->argv[1], "no" ) ) {
+ iarg = 0;
+ } else {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "<%s> invalid value",
+ c->argv[0] );
+ Debug( LDAP_DEBUG_ANY|LDAP_DEBUG_NONE, "%s: %s\n",
+ c->log, c->cr_msg );
+ return ARG_BAD_CONF;
+ }
+ break;
+ }
+ j = (arg_type & ARG_NONZERO) ? 1 : 0;
+ if ( iarg < j && larg < j && barg < (unsigned)j ) {
+ larg = larg ? larg : ( barg ? (long)barg : iarg );
+ snprintf( c->cr_msg, sizeof(c->cr_msg), "<%s> invalid value",
+ c->argv[0] );
+ Debug( LDAP_DEBUG_ANY|LDAP_DEBUG_NONE, "%s: %s\n",
+ c->log, c->cr_msg );
+ return ARG_BAD_CONF;
+ }
+ switch ( arg_type ) {
+ case ARG_ON_OFF:
+ case ARG_INT:
+ c->value_int = iarg;
+ break;
+ case ARG_UINT:
+ c->value_uint = uiarg;
+ break;
+ case ARG_LONG:
+ c->value_long = larg;
+ break;
+ case ARG_ULONG:
+ c->value_ulong = ularg;
+ break;
+ case ARG_BER_LEN_T:
+ c->value_ber_t = barg;
+ break;
+ }
+ }
+ return 0;
+}
+
+int
+config_set_vals( ConfigTable *Conf, ConfigArgs *c )
+{
+ int rc, arg_type;
+ void *ptr = NULL;
+
+ arg_type = Conf->arg_type;
+ if ( arg_type & ARG_MAGIC ) {
+ c->cr_msg[0] = '\0';
+ rc = ( *( (ConfigDriver *)Conf->arg_item ) )( c );
+ if ( rc ) {
+ if ( !c->cr_msg[0] ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "<%s> handler exited with %d",
+ c->argv[0], rc );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s!\n", c->log, c->cr_msg );
+ }
+ return ARG_BAD_CONF;
+ }
+ return 0;
+ }
+ if ( arg_type & ARG_OFFSET ) {
+ {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "<%s> offset is missing base pointer",
+ c->argv[0] );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s!\n", c->log, c->cr_msg );
+ return ARG_BAD_CONF;
+ }
+ ptr = (void *)( (char *)ptr + (long)Conf->arg_item );
+ } else if ( arg_type & ARGS_TYPES ) {
+ ptr = Conf->arg_item;
+ }
+ if ( arg_type & ARGS_TYPES ) switch ( arg_type & ARGS_TYPES ) {
+ case ARG_ON_OFF:
+ case ARG_INT:
+ *(int *)ptr = c->value_int;
+ break;
+ case ARG_UINT:
+ *(unsigned *)ptr = c->value_uint;
+ break;
+ case ARG_LONG:
+ *(long *)ptr = c->value_long;
+ break;
+ case ARG_ULONG:
+ *(size_t *)ptr = c->value_ulong;
+ break;
+ case ARG_BER_LEN_T:
+ *(ber_len_t *)ptr = c->value_ber_t;
+ break;
+ case ARG_STRING: {
+ char *cc = *(char **)ptr;
+ if ( cc ) {
+ if ( (arg_type & ARG_UNIQUE) &&
+ c->op == SLAP_CONFIG_ADD ) {
+ Debug( LDAP_DEBUG_CONFIG, "%s: already set %s!\n",
+ c->log, Conf->name );
+ return ARG_BAD_CONF;
+ }
+ ch_free( cc );
+ }
+ *(char **)ptr = c->value_string;
+ break;
+ }
+ case ARG_BERVAL:
+ case ARG_BINARY:
+ *(struct berval *)ptr = c->value_bv;
+ break;
+ }
+ return 0;
+}
+
+int
+config_add_vals( ConfigTable *Conf, ConfigArgs *c )
+{
+ int rc, arg_type;
+
+ arg_type = Conf->arg_type;
+ if ( arg_type == ARG_IGNORED ) {
+ Debug( LDAP_DEBUG_CONFIG, "%s: keyword <%s> ignored\n",
+ c->log, Conf->name );
+ return 0;
+ }
+ rc = config_check_vals( Conf, c, 0 );
+ if ( rc ) return rc;
+ return config_set_vals( Conf, c );
+}
+
+int
+read_config_file(
+ const char *fname,
+ int depth,
+ ConfigArgs *cf,
+ ConfigTable *cft )
+{
+ FILE *fp;
+ ConfigTable *ct;
+ ConfigArgs *c;
+ int rc;
+ struct stat s;
+
+ c = ch_calloc( 1, sizeof(ConfigArgs) );
+ if ( c == NULL ) {
+ return 1;
+ }
+
+ if ( depth ) {
+ memcpy( c, cf, sizeof(ConfigArgs) );
+ } else {
+ c->depth = depth; /* XXX */
+ }
+
+ c->valx = -1;
+ c->fname = fname;
+ init_config_argv( c );
+
+ if ( stat( fname, &s ) != 0 ) {
+ char ebuf[128];
+ int saved_errno = errno;
+ ldap_syslog = 1;
+ Debug( LDAP_DEBUG_ANY, "could not stat config file \"%s\": %s (%d)\n",
+ fname, AC_STRERROR_R( saved_errno, ebuf, sizeof(ebuf) ),
+ saved_errno );
+ ch_free( c->argv );
+ ch_free( c );
+ return 1;
+ }
+
+ if ( !S_ISREG(s.st_mode) ) {
+ ldap_syslog = 1;
+ Debug( LDAP_DEBUG_ANY, "regular file expected, got \"%s\"\n", fname );
+ ch_free( c->argv );
+ ch_free( c );
+ return 1;
+ }
+
+ fp = fopen( fname, "r" );
+ if ( fp == NULL ) {
+ char ebuf[128];
+ int saved_errno = errno;
+ ldap_syslog = 1;
+ Debug( LDAP_DEBUG_ANY, "could not open config file \"%s\": %s (%d)\n",
+ fname, AC_STRERROR_R( saved_errno, ebuf, sizeof(ebuf) ),
+ saved_errno );
+ ch_free( c->argv );
+ ch_free( c );
+ return 1;
+ }
+
+ Debug( LDAP_DEBUG_CONFIG, "reading config file %s\n", fname );
+
+ fp_getline_init( c );
+
+ c->tline = NULL;
+
+ while ( fp_getline( fp, c ) ) {
+ /* skip comments and blank lines */
+ if ( c->line[0] == '#' || c->line[0] == '\0' ) {
+ continue;
+ }
+
+ snprintf( c->log, sizeof(c->log), "%s: line %d",
+ c->fname, c->lineno );
+
+ c->argc = 0;
+ ch_free( c->tline );
+ if ( config_fp_parse_line( c ) ) {
+ rc = 1;
+ goto done;
+ }
+
+ if ( c->argc < 1 ) {
+ Debug( LDAP_DEBUG_ANY, "%s: bad config line.\n", c->log );
+ rc = 1;
+ goto done;
+ }
+
+ c->op = SLAP_CONFIG_ADD;
+
+ ct = config_find_keyword( cft, c );
+ if ( ct ) {
+ c->table = Cft_Global;
+ rc = config_add_vals( ct, c );
+ if ( !rc ) continue;
+
+ if ( rc & ARGS_USERLAND ) {
+ /* XXX a usertype would be opaque here */
+ Debug( LDAP_DEBUG_CONFIG, "%s: unknown user type <%s>\n",
+ c->log, c->argv[0] );
+ rc = 1;
+ goto done;
+
+ } else if ( rc == ARG_BAD_CONF ) {
+ rc = 1;
+ goto done;
+ }
+
+ } else {
+ Debug( LDAP_DEBUG_ANY, "%s: unknown directive "
+ "<%s> outside backend info and database definitions.\n",
+ c->log, *c->argv );
+ rc = 1;
+ goto done;
+ }
+ }
+
+ rc = 0;
+
+done:
+ ch_free( c->tline );
+ fclose( fp );
+ ch_free( c->argv );
+ ch_free( c );
+ return rc;
+}
+
+int
+read_config( const char *fname, const char *dir )
+{
+ if ( !fname ) fname = LLOADD_DEFAULT_CONFIGFILE;
+
+ cfn = ch_calloc( 1, sizeof(ConfigFile) );
+
+ return read_config_file( fname, 0, NULL, config_back_cf_table );
+}
+
+/* restrictops, allows, disallows, requires, loglevel */
+
+int
+bverb_to_mask( struct berval *bword, slap_verbmasks *v )
+{
+ int i;
+ for ( i = 0; !BER_BVISNULL( &v[i].word ); i++ ) {
+ if ( !ber_bvstrcasecmp( bword, &v[i].word ) ) break;
+ }
+ return i;
+}
+
+int
+verb_to_mask( const char *word, slap_verbmasks *v )
+{
+ struct berval bword;
+ ber_str2bv( word, 0, 0, &bword );
+ return bverb_to_mask( &bword, v );
+}
+
+int
+verbs_to_mask( int argc, char *argv[], slap_verbmasks *v, slap_mask_t *m )
+{
+ int i, j;
+ for ( i = 1; i < argc; i++ ) {
+ j = verb_to_mask( argv[i], v );
+ if ( BER_BVISNULL( &v[j].word ) ) return i;
+ while ( !v[j].mask )
+ j--;
+ *m |= v[j].mask;
+ }
+ return 0;
+}
+
+/* Mask keywords that represent multiple bits should occur before single
+ * bit keywords in the verbmasks array.
+ */
+int
+mask_to_verbs( slap_verbmasks *v, slap_mask_t m, BerVarray *bva )
+{
+ int i, rc = 1;
+
+ if ( m ) {
+ for ( i = 0; !BER_BVISNULL( &v[i].word ); i++ ) {
+ if ( !v[i].mask ) continue;
+ if ( (m & v[i].mask) == v[i].mask ) {
+ value_add_one( bva, &v[i].word );
+ rc = 0;
+ m ^= v[i].mask;
+ if ( !m ) break;
+ }
+ }
+ }
+ return rc;
+}
+
+int
+slap_verbmasks_init( slap_verbmasks **vp, slap_verbmasks *v )
+{
+ int i;
+
+ assert( *vp == NULL );
+
+ for ( i = 0; !BER_BVISNULL( &v[i].word ); i++ ) /* EMPTY */;
+
+ *vp = ch_calloc( i + 1, sizeof(slap_verbmasks) );
+
+ for ( i = 0; !BER_BVISNULL( &v[i].word ); i++ ) {
+ ber_dupbv( &(*vp)[i].word, &v[i].word );
+ *( (slap_mask_t *)&(*vp)[i].mask ) = v[i].mask;
+ }
+
+ BER_BVZERO( &(*vp)[i].word );
+
+ return 0;
+}
+
+int
+slap_verbmasks_destroy( slap_verbmasks *v )
+{
+ int i;
+
+ assert( v != NULL );
+
+ for ( i = 0; !BER_BVISNULL( &v[i].word ); i++ ) {
+ ch_free( v[i].word.bv_val );
+ }
+
+ ch_free( v );
+
+ return 0;
+}
+
+int
+config_push_cleanup( ConfigArgs *ca, ConfigDriver *cleanup )
+{
+ /* Stub, cleanups only run in online config */
+ return 0;
+}
+
+static slap_verbmasks tlskey[] = {
+ { BER_BVC("no"), SB_TLS_OFF },
+ { BER_BVC("yes"), SB_TLS_ON },
+ { BER_BVC("critical"), SB_TLS_CRITICAL },
+ { BER_BVNULL, 0 }
+};
+
+static slap_verbmasks crlkeys[] = {
+ { BER_BVC("none"), LDAP_OPT_X_TLS_CRL_NONE },
+ { BER_BVC("peer"), LDAP_OPT_X_TLS_CRL_PEER },
+ { BER_BVC("all"), LDAP_OPT_X_TLS_CRL_ALL },
+ { BER_BVNULL, 0 }
+};
+
+static slap_verbmasks vfykeys[] = {
+ { BER_BVC("never"), LDAP_OPT_X_TLS_NEVER },
+ { BER_BVC("allow"), LDAP_OPT_X_TLS_ALLOW },
+ { BER_BVC("try"), LDAP_OPT_X_TLS_TRY },
+ { BER_BVC("demand"), LDAP_OPT_X_TLS_DEMAND },
+ { BER_BVC("hard"), LDAP_OPT_X_TLS_HARD },
+ { BER_BVC("true"), LDAP_OPT_X_TLS_HARD },
+ { BER_BVNULL, 0 }
+};
+
+static slap_verbmasks methkey[] = {
+ { BER_BVC("none"), LDAP_AUTH_NONE },
+ { BER_BVC("simple"), LDAP_AUTH_SIMPLE },
+#ifdef HAVE_CYRUS_SASL
+ { BER_BVC("sasl"), LDAP_AUTH_SASL },
+#endif
+ { BER_BVNULL, 0 }
+};
+
+static slap_verbmasks versionkey[] = {
+ { BER_BVC("2"), LDAP_VERSION2 },
+ { BER_BVC("3"), LDAP_VERSION3 },
+ { BER_BVNULL, 0 }
+};
+
+int
+slap_keepalive_parse(
+ struct berval *val,
+ void *bc,
+ slap_cf_aux_table *tab0,
+ const char *tabmsg,
+ int unparse )
+{
+ if ( unparse ) {
+ slap_keepalive *sk = (slap_keepalive *)bc;
+ int rc = snprintf( val->bv_val, val->bv_len, "%d:%d:%d",
+ sk->sk_idle, sk->sk_probes, sk->sk_interval );
+ if ( rc < 0 ) {
+ return -1;
+ }
+
+ if ( (unsigned)rc >= val->bv_len ) {
+ return -1;
+ }
+
+ val->bv_len = rc;
+
+ } else {
+ char *s = val->bv_val;
+ char *next;
+ slap_keepalive *sk = (slap_keepalive *)bc;
+ slap_keepalive sk2;
+
+ if ( s[0] == ':' ) {
+ sk2.sk_idle = 0;
+ s++;
+
+ } else {
+ sk2.sk_idle = strtol( s, &next, 10 );
+ if ( next == s || next[0] != ':' ) {
+ return -1;
+ }
+
+ if ( sk2.sk_idle < 0 ) {
+ return -1;
+ }
+
+ s = ++next;
+ }
+
+ if ( s[0] == ':' ) {
+ sk2.sk_probes = 0;
+ s++;
+
+ } else {
+ sk2.sk_probes = strtol( s, &next, 10 );
+ if ( next == s || next[0] != ':' ) {
+ return -1;
+ }
+
+ if ( sk2.sk_probes < 0 ) {
+ return -1;
+ }
+
+ s = ++next;
+ }
+
+ if ( *s == '\0' ) {
+ sk2.sk_interval = 0;
+
+ } else {
+ sk2.sk_interval = strtol( s, &next, 10 );
+ if ( next == s || next[0] != '\0' ) {
+ return -1;
+ }
+
+ if ( sk2.sk_interval < 0 ) {
+ return -1;
+ }
+ }
+
+ *sk = sk2;
+
+ ber_memfree( val->bv_val );
+ BER_BVZERO( val );
+ }
+
+ return 0;
+}
+
+static int
+slap_sb_uri(
+ struct berval *val,
+ void *bcp,
+ slap_cf_aux_table *tab0,
+ const char *tabmsg,
+ int unparse )
+{
+ slap_bindconf *bc = bcp;
+ if ( unparse ) {
+ if ( bc->sb_uri.bv_len >= val->bv_len ) return -1;
+ val->bv_len = bc->sb_uri.bv_len;
+ AC_MEMCPY( val->bv_val, bc->sb_uri.bv_val, val->bv_len );
+ } else {
+ bc->sb_uri = *val;
+#ifdef HAVE_TLS
+ if ( ldap_is_ldaps_url( val->bv_val ) ) bc->sb_tls_do_init = 1;
+#endif
+ }
+ return 0;
+}
+
+static slap_cf_aux_table bindkey[] = {
+ { BER_BVC("uri="), 0, 'x', 1, slap_sb_uri },
+ { BER_BVC("version="), offsetof(slap_bindconf, sb_version), 'i', 0, versionkey },
+ { BER_BVC("bindmethod="), offsetof(slap_bindconf, sb_method), 'i', 0, methkey },
+ { BER_BVC("timeout="), offsetof(slap_bindconf, sb_timeout_api), 'i', 0, NULL },
+ { BER_BVC("network-timeout="), offsetof(slap_bindconf, sb_timeout_net), 'i', 0, NULL },
+ { BER_BVC("binddn="), offsetof(slap_bindconf, sb_binddn), 'b', 1, NULL },
+ { BER_BVC("credentials="), offsetof(slap_bindconf, sb_cred), 'b', 1, NULL },
+ { BER_BVC("saslmech="), offsetof(slap_bindconf, sb_saslmech), 'b', 0, NULL },
+ { BER_BVC("secprops="), offsetof(slap_bindconf, sb_secprops), 's', 0, NULL },
+ { BER_BVC("realm="), offsetof(slap_bindconf, sb_realm), 'b', 0, NULL },
+ { BER_BVC("authcID="), offsetof(slap_bindconf, sb_authcId), 'b', 1, NULL },
+ { BER_BVC("authzID="), offsetof(slap_bindconf, sb_authzId), 'b', 1, NULL },
+ { BER_BVC("keepalive="), offsetof(slap_bindconf, sb_keepalive), 'x', 0, (slap_verbmasks *)slap_keepalive_parse },
+#ifdef HAVE_TLS
+ { BER_BVC("starttls="), offsetof(slap_bindconf, sb_tls), 'i', 0, tlskey },
+ { BER_BVC("tls_cert="), offsetof(slap_bindconf, sb_tls_cert), 's', 1, NULL },
+ { BER_BVC("tls_key="), offsetof(slap_bindconf, sb_tls_key), 's', 1, NULL },
+ { BER_BVC("tls_cacert="), offsetof(slap_bindconf, sb_tls_cacert), 's', 1, NULL },
+ { BER_BVC("tls_cacertdir="), offsetof(slap_bindconf, sb_tls_cacertdir), 's', 1, NULL },
+ { BER_BVC("tls_reqcert="), offsetof(slap_bindconf, sb_tls_reqcert), 's', 0, NULL },
+ { BER_BVC("tls_reqsan="), offsetof(slap_bindconf, sb_tls_reqsan), 's', 0, NULL },
+ { BER_BVC("tls_cipher_suite="), offsetof(slap_bindconf, sb_tls_cipher_suite), 's', 0, NULL },
+ { BER_BVC("tls_protocol_min="), offsetof(slap_bindconf, sb_tls_protocol_min), 's', 0, NULL },
+ { BER_BVC("tls_ecname="), offsetof(slap_bindconf, sb_tls_ecname), 's', 0, NULL },
+#ifdef HAVE_OPENSSL
+ { BER_BVC("tls_crlcheck="), offsetof(slap_bindconf, sb_tls_crlcheck), 's', 0, NULL },
+#endif
+#endif
+ { BER_BVNULL, 0, 0, 0, NULL }
+};
+
+/*
+ * 's': char *
+ * 'b': struct berval
+ * 'i': int; if !NULL, compute using ((slap_verbmasks *)aux)
+ * 'u': unsigned
+ * 'I': long
+ * 'U': unsigned long
+ */
+
+int
+slap_cf_aux_table_parse(
+ const char *word,
+ void *dst,
+ slap_cf_aux_table *tab0,
+ LDAP_CONST char *tabmsg )
+{
+ int rc = SLAP_CONF_UNKNOWN;
+ slap_cf_aux_table *tab;
+
+ for ( tab = tab0; !BER_BVISNULL( &tab->key ); tab++ ) {
+ if ( !strncasecmp( word, tab->key.bv_val, tab->key.bv_len ) ) {
+ char **cptr;
+ int *iptr, j;
+ unsigned *uptr;
+ long *lptr;
+ unsigned long *ulptr;
+ struct berval *bptr;
+ const char *val = word + tab->key.bv_len;
+
+ switch ( tab->type ) {
+ case 's':
+ cptr = (char **)( (char *)dst + tab->off );
+ *cptr = ch_strdup( val );
+ rc = 0;
+ break;
+
+ case 'b':
+ bptr = (struct berval *)( (char *)dst + tab->off );
+ assert( tab->aux == NULL );
+ ber_str2bv( val, 0, 1, bptr );
+ rc = 0;
+ break;
+
+ case 'i':
+ iptr = (int *)( (char *)dst + tab->off );
+
+ if ( tab->aux != NULL ) {
+ slap_verbmasks *aux = (slap_verbmasks *)tab->aux;
+
+ assert( aux != NULL );
+
+ rc = 1;
+ for ( j = 0; !BER_BVISNULL( &aux[j].word ); j++ ) {
+ if ( !strcasecmp( val, aux[j].word.bv_val ) ) {
+ *iptr = aux[j].mask;
+ rc = 0;
+ break;
+ }
+ }
+
+ } else {
+ rc = lutil_atoix( iptr, val, 0 );
+ }
+ break;
+
+ case 'u':
+ uptr = (unsigned *)( (char *)dst + tab->off );
+
+ rc = lutil_atoux( uptr, val, 0 );
+ break;
+
+ case 'I':
+ lptr = (long *)( (char *)dst + tab->off );
+
+ rc = lutil_atolx( lptr, val, 0 );
+ break;
+
+ case 'U':
+ ulptr = (unsigned long *)( (char *)dst + tab->off );
+
+ rc = lutil_atoulx( ulptr, val, 0 );
+ break;
+
+ case 'x':
+ if ( tab->aux != NULL ) {
+ struct berval value;
+ slap_cf_aux_table_parse_x *func =
+ (slap_cf_aux_table_parse_x *)tab->aux;
+
+ ber_str2bv( val, 0, 1, &value );
+
+ rc = func( &value, (void *)( (char *)dst + tab->off ),
+ tab, tabmsg, 0 );
+
+ } else {
+ rc = 1;
+ }
+ break;
+ }
+
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY, "invalid %s value %s\n", tabmsg, word );
+ }
+
+ return rc;
+ }
+ }
+
+ return rc;
+}
+
+int
+slap_cf_aux_table_unparse(
+ void *src,
+ struct berval *bv,
+ slap_cf_aux_table *tab0 )
+{
+ char buf[AC_LINE_MAX], *ptr;
+ slap_cf_aux_table *tab;
+ struct berval tmp;
+
+ ptr = buf;
+ for ( tab = tab0; !BER_BVISNULL( &tab->key ); tab++ ) {
+ char **cptr;
+ int *iptr, i;
+ unsigned *uptr;
+ long *lptr;
+ unsigned long *ulptr;
+ struct berval *bptr;
+
+ cptr = (char **)( (char *)src + tab->off );
+
+ switch ( tab->type ) {
+ case 'b':
+ bptr = (struct berval *)( (char *)src + tab->off );
+ cptr = &bptr->bv_val;
+
+ case 's':
+ if ( *cptr ) {
+ *ptr++ = ' ';
+ ptr = lutil_strcopy( ptr, tab->key.bv_val );
+ if ( tab->quote ) *ptr++ = '"';
+ ptr = lutil_strcopy( ptr, *cptr );
+ if ( tab->quote ) *ptr++ = '"';
+ }
+ break;
+
+ case 'i':
+ iptr = (int *)( (char *)src + tab->off );
+
+ if ( tab->aux != NULL ) {
+ slap_verbmasks *aux = (slap_verbmasks *)tab->aux;
+
+ for ( i = 0; !BER_BVISNULL( &aux[i].word ); i++ ) {
+ if ( *iptr == aux[i].mask ) {
+ *ptr++ = ' ';
+ ptr = lutil_strcopy( ptr, tab->key.bv_val );
+ ptr = lutil_strcopy( ptr, aux[i].word.bv_val );
+ break;
+ }
+ }
+
+ } else {
+ *ptr++ = ' ';
+ ptr = lutil_strcopy( ptr, tab->key.bv_val );
+ ptr += snprintf( ptr, sizeof(buf) - ( ptr - buf ), "%d",
+ *iptr );
+ }
+ break;
+
+ case 'u':
+ uptr = (unsigned *)( (char *)src + tab->off );
+ *ptr++ = ' ';
+ ptr = lutil_strcopy( ptr, tab->key.bv_val );
+ ptr += snprintf( ptr, sizeof(buf) - ( ptr - buf ), "%u",
+ *uptr );
+ break;
+
+ case 'I':
+ lptr = (long *)( (char *)src + tab->off );
+ *ptr++ = ' ';
+ ptr = lutil_strcopy( ptr, tab->key.bv_val );
+ ptr += snprintf( ptr, sizeof(buf) - ( ptr - buf ), "%ld",
+ *lptr );
+ break;
+
+ case 'U':
+ ulptr = (unsigned long *)( (char *)src + tab->off );
+ *ptr++ = ' ';
+ ptr = lutil_strcopy( ptr, tab->key.bv_val );
+ ptr += snprintf( ptr, sizeof(buf) - ( ptr - buf ), "%lu",
+ *ulptr );
+ break;
+
+ case 'x': {
+ char *saveptr = ptr;
+ *ptr++ = ' ';
+ ptr = lutil_strcopy( ptr, tab->key.bv_val );
+ if ( tab->quote ) *ptr++ = '"';
+ if ( tab->aux != NULL ) {
+ struct berval value;
+ slap_cf_aux_table_parse_x *func =
+ (slap_cf_aux_table_parse_x *)tab->aux;
+ int rc;
+
+ value.bv_val = ptr;
+ value.bv_len = buf + sizeof(buf) - ptr;
+
+ rc = func( &value, (void *)( (char *)src + tab->off ), tab,
+ "(unparse)", 1 );
+ if ( rc == 0 ) {
+ if ( value.bv_len ) {
+ ptr += value.bv_len;
+ } else {
+ ptr = saveptr;
+ break;
+ }
+ }
+ }
+ if ( tab->quote ) *ptr++ = '"';
+ } break;
+
+ default:
+ assert(0);
+ }
+ }
+ tmp.bv_val = buf;
+ tmp.bv_len = ptr - buf;
+ ber_dupbv( bv, &tmp );
+ return 0;
+}
+
+int
+slap_tls_get_config( LDAP *ld, int opt, char **val )
+{
+#ifdef HAVE_TLS
+ slap_verbmasks *keys;
+ int i, ival;
+
+ *val = NULL;
+ switch ( opt ) {
+ case LDAP_OPT_X_TLS_CRLCHECK:
+ keys = crlkeys;
+ break;
+ case LDAP_OPT_X_TLS_REQUIRE_CERT:
+ keys = vfykeys;
+ break;
+ case LDAP_OPT_X_TLS_PROTOCOL_MIN: {
+ char buf[8];
+ ldap_pvt_tls_get_option( ld, opt, &ival );
+ snprintf( buf, sizeof(buf), "%d.%d",
+ ( ival >> 8 ) & 0xff, ival & 0xff );
+ *val = ch_strdup( buf );
+ return 0;
+ }
+ default:
+ return -1;
+ }
+ ldap_pvt_tls_get_option( ld, opt, &ival );
+ for ( i = 0; !BER_BVISNULL( &keys[i].word ); i++ ) {
+ if ( keys[i].mask == ival ) {
+ *val = ch_strdup( keys[i].word.bv_val );
+ return 0;
+ }
+ }
+#endif
+ return -1;
+}
+
+int
+bindconf_parse( const char *word, slap_bindconf *bc )
+{
+ return slap_cf_aux_table_parse( word, bc, bindkey, "bind config" );
+}
+
+int
+bindconf_unparse( slap_bindconf *bc, struct berval *bv )
+{
+ return slap_cf_aux_table_unparse( bc, bv, bindkey );
+}
+
+void
+bindconf_free( slap_bindconf *bc )
+{
+ if ( !BER_BVISNULL( &bc->sb_uri ) ) {
+ ch_free( bc->sb_uri.bv_val );
+ BER_BVZERO( &bc->sb_uri );
+ }
+ if ( !BER_BVISNULL( &bc->sb_binddn ) ) {
+ ch_free( bc->sb_binddn.bv_val );
+ BER_BVZERO( &bc->sb_binddn );
+ }
+ if ( !BER_BVISNULL( &bc->sb_cred ) ) {
+ ch_free( bc->sb_cred.bv_val );
+ BER_BVZERO( &bc->sb_cred );
+ }
+ if ( !BER_BVISNULL( &bc->sb_saslmech ) ) {
+ ch_free( bc->sb_saslmech.bv_val );
+ BER_BVZERO( &bc->sb_saslmech );
+ }
+ if ( bc->sb_secprops ) {
+ ch_free( bc->sb_secprops );
+ bc->sb_secprops = NULL;
+ }
+ if ( !BER_BVISNULL( &bc->sb_realm ) ) {
+ ch_free( bc->sb_realm.bv_val );
+ BER_BVZERO( &bc->sb_realm );
+ }
+ if ( !BER_BVISNULL( &bc->sb_authcId ) ) {
+ ch_free( bc->sb_authcId.bv_val );
+ BER_BVZERO( &bc->sb_authcId );
+ }
+ if ( !BER_BVISNULL( &bc->sb_authzId ) ) {
+ ch_free( bc->sb_authzId.bv_val );
+ BER_BVZERO( &bc->sb_authzId );
+ }
+#ifdef HAVE_TLS
+ if ( bc->sb_tls_cert ) {
+ ch_free( bc->sb_tls_cert );
+ bc->sb_tls_cert = NULL;
+ }
+ if ( bc->sb_tls_key ) {
+ ch_free( bc->sb_tls_key );
+ bc->sb_tls_key = NULL;
+ }
+ if ( bc->sb_tls_cacert ) {
+ ch_free( bc->sb_tls_cacert );
+ bc->sb_tls_cacert = NULL;
+ }
+ if ( bc->sb_tls_cacertdir ) {
+ ch_free( bc->sb_tls_cacertdir );
+ bc->sb_tls_cacertdir = NULL;
+ }
+ if ( bc->sb_tls_reqcert ) {
+ ch_free( bc->sb_tls_reqcert );
+ bc->sb_tls_reqcert = NULL;
+ }
+ if ( bc->sb_tls_cipher_suite ) {
+ ch_free( bc->sb_tls_cipher_suite );
+ bc->sb_tls_cipher_suite = NULL;
+ }
+ if ( bc->sb_tls_protocol_min ) {
+ ch_free( bc->sb_tls_protocol_min );
+ bc->sb_tls_protocol_min = NULL;
+ }
+#ifdef HAVE_OPENSSL_CRL
+ if ( bc->sb_tls_crlcheck ) {
+ ch_free( bc->sb_tls_crlcheck );
+ bc->sb_tls_crlcheck = NULL;
+ }
+#endif
+ if ( bc->sb_tls_ctx ) {
+ ldap_pvt_tls_ctx_free( bc->sb_tls_ctx );
+ bc->sb_tls_ctx = NULL;
+ }
+#endif
+}
+
+void
+bindconf_tls_defaults( slap_bindconf *bc )
+{
+#ifdef HAVE_TLS
+ if ( bc->sb_tls_do_init ) {
+ if ( !bc->sb_tls_cacert )
+ ldap_pvt_tls_get_option( slap_tls_ld, LDAP_OPT_X_TLS_CACERTFILE,
+ &bc->sb_tls_cacert );
+ if ( !bc->sb_tls_cacertdir )
+ ldap_pvt_tls_get_option( slap_tls_ld, LDAP_OPT_X_TLS_CACERTDIR,
+ &bc->sb_tls_cacertdir );
+ if ( !bc->sb_tls_cert )
+ ldap_pvt_tls_get_option(
+ slap_tls_ld, LDAP_OPT_X_TLS_CERTFILE, &bc->sb_tls_cert );
+ if ( !bc->sb_tls_key )
+ ldap_pvt_tls_get_option(
+ slap_tls_ld, LDAP_OPT_X_TLS_KEYFILE, &bc->sb_tls_key );
+ if ( !bc->sb_tls_cipher_suite )
+ ldap_pvt_tls_get_option( slap_tls_ld, LDAP_OPT_X_TLS_CIPHER_SUITE,
+ &bc->sb_tls_cipher_suite );
+ if ( !bc->sb_tls_reqcert ) bc->sb_tls_reqcert = ch_strdup( "demand" );
+#ifdef HAVE_OPENSSL_CRL
+ if ( !bc->sb_tls_crlcheck )
+ slap_tls_get_config( slap_tls_ld, LDAP_OPT_X_TLS_CRLCHECK,
+ &bc->sb_tls_crlcheck );
+#endif
+ }
+#endif
+}
+
+/* -------------------------------------- */
+
+static char *
+strtok_quote( char *line, char *sep, char **quote_ptr, int *iqp )
+{
+ int inquote;
+ char *tmp;
+ static char *next;
+
+ *quote_ptr = NULL;
+ if ( line != NULL ) {
+ next = line;
+ }
+ while ( *next && strchr( sep, *next ) ) {
+ next++;
+ }
+
+ if ( *next == '\0' ) {
+ next = NULL;
+ return NULL;
+ }
+ tmp = next;
+
+ for ( inquote = 0; *next; ) {
+ switch ( *next ) {
+ case '"':
+ if ( inquote ) {
+ inquote = 0;
+ } else {
+ inquote = 1;
+ }
+ AC_MEMCPY( next, next + 1, strlen( next + 1 ) + 1 );
+ break;
+
+ case '\\':
+ if ( next[1] )
+ AC_MEMCPY( next, next + 1, strlen( next + 1 ) + 1 );
+ next++; /* dont parse the escaped character */
+ break;
+
+ default:
+ if ( !inquote ) {
+ if ( strchr( sep, *next ) != NULL ) {
+ *quote_ptr = next;
+ *next++ = '\0';
+ return tmp;
+ }
+ }
+ next++;
+ break;
+ }
+ }
+ *iqp = inquote;
+
+ return tmp;
+}
+
+static char buf[AC_LINE_MAX];
+static char *line;
+static size_t lmax, lcur;
+
+#define CATLINE( buf ) \
+ do { \
+ size_t len = strlen( buf ); \
+ while ( lcur + len + 1 > lmax ) { \
+ lmax += AC_LINE_MAX; \
+ line = (char *)ch_realloc( line, lmax ); \
+ } \
+ strcpy( line + lcur, buf ); \
+ lcur += len; \
+ } while (0)
+
+static void
+fp_getline_init( ConfigArgs *c )
+{
+ c->lineno = -1;
+ buf[0] = '\0';
+}
+
+static int
+fp_getline( FILE *fp, ConfigArgs *c )
+{
+ char *p;
+
+ lcur = 0;
+ CATLINE( buf );
+ c->lineno++;
+
+ /* avoid stack of bufs */
+ if ( strncasecmp( line, "include", STRLENOF("include") ) == 0 ) {
+ buf[0] = '\0';
+ c->line = line;
+ return 1;
+ }
+
+ while ( fgets( buf, sizeof(buf), fp ) ) {
+ p = strchr( buf, '\n' );
+ if ( p ) {
+ if ( p > buf && p[-1] == '\r' ) {
+ --p;
+ }
+ *p = '\0';
+ }
+ /* XXX ugly */
+ c->line = line;
+ if ( line[0] && ( p = line + strlen( line ) - 1 )[0] == '\\' &&
+ p[-1] != '\\' ) {
+ p[0] = '\0';
+ lcur--;
+
+ } else {
+ if ( !isspace( (unsigned char)buf[0] ) ) {
+ return 1;
+ }
+ buf[0] = ' ';
+ }
+ CATLINE( buf );
+ c->lineno++;
+ }
+
+ buf[0] = '\0';
+ c->line = line;
+ return ( line[0] ? 1 : 0 );
+}
+
+int
+config_fp_parse_line( ConfigArgs *c )
+{
+ char *token;
+ static char *const hide[] = { "bindconf", NULL };
+ static char *const raw[] = { NULL };
+ char *quote_ptr;
+ int i = (int)( sizeof(hide) / sizeof(hide[0]) ) - 1;
+ int inquote = 0;
+
+ c->tline = ch_strdup( c->line );
+ c->linelen = strlen( c->line );
+ token = strtok_quote( c->tline, " \t", "e_ptr, &inquote );
+
+ if ( token )
+ for ( i = 0; hide[i]; i++ )
+ if ( !strcasecmp( token, hide[i] ) ) break;
+ if ( quote_ptr ) *quote_ptr = ' ';
+ Debug( LDAP_DEBUG_CONFIG, "%s (%s%s)\n",
+ c->log, hide[i] ? hide[i] : c->line, hide[i] ? " ***" : "" );
+ if ( quote_ptr ) *quote_ptr = '\0';
+
+ for ( ;; token = strtok_quote( NULL, " \t", "e_ptr, &inquote ) ) {
+ if ( c->argc >= c->argv_size ) {
+ char **tmp;
+ tmp = ch_realloc( c->argv,
+ ( c->argv_size + ARGS_STEP ) * sizeof(*c->argv) );
+ if ( !tmp ) {
+ Debug( LDAP_DEBUG_ANY, "%s: out of memory\n", c->log );
+ return -1;
+ }
+ c->argv = tmp;
+ c->argv_size += ARGS_STEP;
+ }
+ if ( token == NULL ) break;
+ c->argv[c->argc++] = token;
+ }
+ c->argv[c->argc] = NULL;
+ if ( inquote ) {
+ /* these directives parse c->line independently of argv tokenizing */
+ for ( i = 0; raw[i]; i++ )
+ if ( !strcasecmp( c->argv[0], raw[i] ) ) return 0;
+
+ Debug( LDAP_DEBUG_ANY, "%s: unterminated quoted string \"%s\"\n",
+ c->log, c->argv[c->argc - 1] );
+ return -1;
+ }
+ return 0;
+}
+
+void
+config_destroy( void )
+{
+ free( line );
+ if ( slapd_args_file ) free( slapd_args_file );
+ if ( slapd_pid_file ) free( slapd_pid_file );
+ loglevel_destroy();
+}
+
+/* See if the given URL (in plain and parsed form) matches
+ * any of the server's listener addresses. Return matching
+ * Listener or NULL for no match.
+ */
+Listener *
+config_check_my_url( const char *url, LDAPURLDesc *lud )
+{
+ Listener **l = slapd_get_listeners();
+ int i, isMe;
+
+ /* Try a straight compare with Listener strings */
+ for ( i = 0; l && l[i]; i++ ) {
+ if ( !strcasecmp( url, l[i]->sl_url.bv_val ) ) {
+ return l[i];
+ }
+ }
+
+ isMe = 0;
+ /* If hostname is empty, or is localhost, or matches
+ * our hostname, this url refers to this host.
+ * Compare it against listeners and ports.
+ */
+ if ( !lud->lud_host || !lud->lud_host[0] ||
+ !strncasecmp(
+ "localhost", lud->lud_host, STRLENOF("localhost") ) ||
+ !strcasecmp( global_host, lud->lud_host ) ) {
+ for ( i = 0; l && l[i]; i++ ) {
+ LDAPURLDesc *lu2;
+ ldap_url_parse( l[i]->sl_url.bv_val, &lu2 );
+ do {
+ if ( strcasecmp( lud->lud_scheme, lu2->lud_scheme ) ) break;
+ if ( lud->lud_port != lu2->lud_port ) break;
+ /* Listener on ANY address */
+ if ( !lu2->lud_host || !lu2->lud_host[0] ) {
+ isMe = 1;
+ break;
+ }
+ /* URL on ANY address */
+ if ( !lud->lud_host || !lud->lud_host[0] ) {
+ isMe = 1;
+ break;
+ }
+ /* Listener has specific host, must
+ * match it
+ */
+ if ( !strcasecmp( lud->lud_host, lu2->lud_host ) ) {
+ isMe = 1;
+ break;
+ }
+ } while (0);
+ ldap_free_urldesc( lu2 );
+ if ( isMe ) {
+ return l[i];
+ }
+ }
+ }
+ return NULL;
+}
--- /dev/null
+/* config.h - configuration abstraction structure */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2020 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#include <ac/string.h>
+
+LDAP_BEGIN_DECL
+
+typedef struct ConfigTable {
+ const char *name;
+ const char *what;
+ int min_args;
+ int max_args;
+ int length;
+ unsigned int arg_type;
+ void *arg_item;
+} ConfigTable;
+
+/* search entries are returned according to this order */
+typedef enum {
+ Cft_Abstract = 0,
+ Cft_Global,
+ Cft_Module,
+ Cft_Schema,
+ Cft_Backend,
+ Cft_Database,
+ Cft_Overlay,
+ Cft_Misc /* backend/overlay defined */
+} ConfigType;
+
+#define ARGS_USERLAND 0x00000fff
+
+/* types are enumerated, not a bitmask */
+#define ARGS_TYPES 0x0000f000
+#define ARG_INT 0x00001000
+#define ARG_LONG 0x00002000
+#define ARG_BER_LEN_T 0x00003000
+#define ARG_ON_OFF 0x00004000
+#define ARG_STRING 0x00005000
+#define ARG_BERVAL 0x00006000
+#define ARG_UINT 0x00008000
+#define ARG_ULONG 0x0000a000
+#define ARG_BINARY 0x0000b000
+
+#define ARGS_SYNTAX 0xffff0000
+#define ARG_IGNORED 0x00080000
+#define ARG_PAREN 0x01000000
+#define ARG_NONZERO 0x02000000
+#define ARG_NO_INSERT 0x04000000 /* no arbitrary inserting */
+#define ARG_NO_DELETE 0x08000000 /* no runtime deletes */
+#define ARG_UNIQUE 0x10000000
+#define ARG_QUOTE 0x20000000 /* wrap with quotes before parsing */
+#define ARG_OFFSET 0x40000000
+#define ARG_MAGIC 0x80000000
+
+#define ARG_BAD_CONF 0xdead0000 /* overload return values */
+
+struct config_args_s;
+
+typedef int (ConfigDriver)( struct config_args_s *c );
+
+struct config_reply_s {
+ int err;
+ char msg[SLAP_TEXT_BUFLEN];
+};
+
+typedef struct config_args_s {
+ int argc;
+ char **argv;
+ int argv_size;
+ char *line;
+ char *tline;
+ const char *fname;
+ int lineno;
+ int linelen;
+ char log[MAXPATHLEN + STRLENOF(": line ") +
+ LDAP_PVT_INTTYPE_CHARS(unsigned long)];
+#define cr_msg reply.msg
+ ConfigReply reply;
+ int depth;
+ int valx; /* multi-valued value index */
+ /* parsed first val for simple cases */
+ union {
+ int v_int;
+ unsigned v_uint;
+ long v_long;
+ size_t v_ulong;
+ ber_len_t v_ber_t;
+ char *v_string;
+ struct berval v_bv;
+ } values;
+ /* return values for emit mode */
+ BerVarray rvalue_vals;
+ BerVarray rvalue_nvals;
+#define SLAP_CONFIG_EMIT 0x2000 /* emit instead of set */
+#define SLAP_CONFIG_ADD 0x4000 /* config file add vs LDAP add */
+ int op;
+ int type; /* ConfigTable.arg_type & ARGS_USERLAND */
+ void *ca_private; /* anything */
+#ifndef SLAP_CONFIG_CLEANUP_MAX
+#define SLAP_CONFIG_CLEANUP_MAX 16
+#endif
+ ConfigDriver *cleanups[SLAP_CONFIG_CLEANUP_MAX];
+ ConfigType table; /* which config table did we come from */
+ int num_cleanups;
+} ConfigArgs;
+
+#define value_int values.v_int
+#define value_uint values.v_uint
+#define value_long values.v_long
+#define value_ulong values.v_ulong
+#define value_ber_t values.v_ber_t
+#define value_string values.v_string
+#define value_bv values.v_bv
+
+int config_fp_parse_line( ConfigArgs *c );
+
+int config_set_vals( ConfigTable *ct, ConfigArgs *c );
+int config_add_vals( ConfigTable *ct, ConfigArgs *c );
+
+int config_push_cleanup( ConfigArgs *c, ConfigDriver *cleanup );
+
+void init_config_argv( ConfigArgs *c );
+int read_config_file( const char *fname, int depth, ConfigArgs *cf, ConfigTable *cft );
+
+ConfigTable *config_find_keyword( ConfigTable *ct, ConfigArgs *c );
+
+Listener *config_check_my_url( const char *url, LDAPURLDesc *lud );
+
+LDAP_END_DECL
+
+#endif /* CONFIG_H */
--- /dev/null
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2020 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#include <ac/socket.h>
+#include <ac/errno.h>
+#include <ac/string.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+
+#include "lutil.h"
+#include "slap.h"
+
+static ldap_pvt_thread_mutex_t conn_nextid_mutex;
+static unsigned long conn_nextid = 0;
+
+static void
+connection_assign_nextid( Connection *conn )
+{
+ ldap_pvt_thread_mutex_lock( &conn_nextid_mutex );
+ conn->c_connid = conn_nextid++;
+ ldap_pvt_thread_mutex_unlock( &conn_nextid_mutex );
+}
+
+void
+connection_destroy( Connection *c )
+{
+ assert( c );
+
+ ldap_pvt_thread_mutex_destroy( &c->c_io_mutex );
+ ldap_pvt_thread_mutex_destroy( &c->c_mutex );
+
+ ber_sockbuf_free( c->c_sb );
+ ch_free( c );
+}
+
+Connection *
+connection_init( ber_socket_t s, const char *peername, int flags )
+{
+ Connection *c;
+
+ assert( peername != NULL );
+
+#ifndef HAVE_TLS
+ assert( !(flags & CONN_IS_TLS) );
+#endif
+
+ if ( s == AC_SOCKET_INVALID ) {
+ Debug( LDAP_DEBUG_ANY, "connection_init: "
+ "init of socket fd=%ld invalid.\n",
+ (long)s );
+ return NULL;
+ }
+
+ assert( s >= 0 );
+
+ c = ch_calloc( 1, sizeof(Connection) );
+
+ c->c_sb = ber_sockbuf_alloc();
+ ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_SET_FD, &s );
+
+ {
+ ber_len_t max = sockbuf_max_incoming;
+ ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_SET_MAX_INCOMING, &max );
+ }
+
+#ifdef LDAP_PF_LOCAL
+ if ( flags & CONN_IS_IPC ) {
+#ifdef LDAP_DEBUG
+ ber_sockbuf_add_io( c->c_sb, &ber_sockbuf_io_debug,
+ LBER_SBIOD_LEVEL_PROVIDER, (void *)"ipc_" );
+#endif
+ ber_sockbuf_add_io( c->c_sb, &ber_sockbuf_io_fd,
+ LBER_SBIOD_LEVEL_PROVIDER, (void *)&s );
+#ifdef LDAP_PF_LOCAL_SENDMSG
+ if ( !BER_BVISEMPTY( peerbv ) )
+ ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_UNGET_BUF, peerbv );
+#endif
+ } else
+#endif /* LDAP_PF_LOCAL */
+ {
+#ifdef LDAP_DEBUG
+ ber_sockbuf_add_io( c->c_sb, &ber_sockbuf_io_debug,
+ LBER_SBIOD_LEVEL_PROVIDER, (void *)"tcp_" );
+#endif
+ ber_sockbuf_add_io( c->c_sb, &ber_sockbuf_io_tcp,
+ LBER_SBIOD_LEVEL_PROVIDER, (void *)&s );
+ }
+
+#ifdef LDAP_DEBUG
+ ber_sockbuf_add_io(
+ c->c_sb, &ber_sockbuf_io_debug, INT_MAX, (void *)"lload_" );
+#endif
+
+#ifdef HAVE_TLS
+ if ( flags & CONN_IS_TLS ) {
+ /* TODO: will need an asynchronous TLS implementation in libldap */
+ assert(0);
+ c->c_is_tls = 1;
+ c->c_needs_tls_accept = 1;
+ } else {
+ c->c_is_tls = 0;
+ c->c_needs_tls_accept = 0;
+ }
+#endif
+
+ ldap_pvt_thread_mutex_init( &c->c_mutex );
+ ldap_pvt_thread_mutex_init( &c->c_io_mutex );
+
+ connection_assign_nextid( c );
+
+ Debug( LDAP_DEBUG_CONNS, "connection_init: "
+ "connection connid=%lu allocated for socket fd=%d\n",
+ c->c_connid, s );
+
+ return c;
+fail:
+ connection_destroy( c );
+ return NULL;
+}
--- /dev/null
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2015 The OpenLDAP Foundation.
+ * Portions Copyright 2007 by Howard Chu, Symas Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/ctype.h>
+#include <ac/errno.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+
+#include <event2/event.h>
+#include <event2/listener.h>
+
+#include "slap.h"
+#include "ldap_pvt_thread.h"
+#include "lutil.h"
+
+#include "ldap_rq.h"
+
+#ifdef HAVE_TCPD
+int allow_severity = LOG_INFO;
+int deny_severity = LOG_NOTICE;
+#endif /* TCP Wrappers */
+
+#ifdef LDAP_PF_LOCAL
+#include <sys/stat.h>
+/* this should go in <ldap.h> as soon as it is accepted */
+#define LDAPI_MOD_URLEXT "x-mod"
+#endif /* LDAP_PF_LOCAL */
+
+#ifdef LDAP_PF_INET6
+int slap_inet4or6 = AF_UNSPEC;
+#else /* ! INETv6 */
+int slap_inet4or6 = AF_INET;
+#endif /* ! INETv6 */
+
+/* globals */
+time_t starttime;
+struct runqueue_s slapd_rq;
+
+#ifndef SLAPD_MAX_DAEMON_THREADS
+#define SLAPD_MAX_DAEMON_THREADS 16
+#endif
+int slapd_daemon_threads = 1;
+int slapd_daemon_mask;
+
+#ifdef LDAP_TCP_BUFFER
+int slapd_tcp_rmem;
+int slapd_tcp_wmem;
+#endif /* LDAP_TCP_BUFFER */
+
+struct event_base *listener_base = NULL;
+Listener **slap_listeners = NULL;
+static volatile sig_atomic_t listening = 1; /* 0 when slap_listeners closed */
+static ldap_pvt_thread_t listener_tid, *daemon_tid;
+
+#ifndef SLAPD_LISTEN_BACKLOG
+#define SLAPD_LISTEN_BACKLOG 1024
+#endif /* ! SLAPD_LISTEN_BACKLOG */
+
+#define DAEMON_ID(fd) ( fd & slapd_daemon_mask )
+
+static int emfile;
+
+static volatile int waking;
+#define WAKE_DAEMON( l, w ) \
+ do { \
+ if ( w ) { \
+ event_active( slap_daemon[l].wakeup_event, EV_WRITE, 0 ); \
+ } \
+ } while (0)
+
+volatile sig_atomic_t slapd_shutdown = 0;
+volatile sig_atomic_t slapd_gentle_shutdown = 0;
+volatile sig_atomic_t slapd_abrupt_shutdown = 0;
+
+#ifdef HAVE_WINSOCK
+ldap_pvt_thread_mutex_t slapd_ws_mutex;
+SOCKET *slapd_ws_sockets;
+#define SD_READ 1
+#define SD_WRITE 2
+#define SD_ACTIVE 4
+#define SD_LISTENER 8
+#endif
+
+#ifdef HAVE_TCPD
+static ldap_pvt_thread_mutex_t sd_tcpd_mutex;
+#endif /* TCP Wrappers */
+
+typedef struct listener_item {
+ struct evconnlistener *listener;
+ ber_socket_t fd;
+} listener_item;
+
+typedef struct slap_daemon_st {
+ ldap_pvt_thread_mutex_t sd_mutex;
+
+ struct event_base *base;
+ struct event *wakeup_event;
+} slap_daemon_st;
+
+static slap_daemon_st slap_daemon[SLAPD_MAX_DAEMON_THREADS];
+
+static void daemon_wakeup_cb( evutil_socket_t sig, short what, void *arg );
+
+/*
+ * Remove the descriptor from daemon control
+ */
+void
+slapd_remove( ber_socket_t s, Sockbuf *sb, int wasactive, int wake, int locked )
+{
+ int id = DAEMON_ID(s);
+
+ if ( !locked ) ldap_pvt_thread_mutex_lock( &slap_daemon[id].sd_mutex );
+
+ if ( sb ) ber_sockbuf_free( sb );
+
+ /* If we ran out of file descriptors, we dropped a listener from
+ * the select() loop. Now that we're removing a session from our
+ * control, we can try to resume a dropped listener to use.
+ */
+ if ( emfile && listening ) {
+ int i;
+ for ( i = 0; slap_listeners[i] != NULL; i++ ) {
+ Listener *lr = slap_listeners[i];
+
+ if ( lr->sl_sd == AC_SOCKET_INVALID ) continue;
+ if ( lr->sl_sd == s ) continue;
+ if ( lr->sl_mute ) {
+ lr->sl_mute = 0;
+ emfile--;
+ if ( DAEMON_ID(lr->sl_sd) != id )
+ WAKE_DAEMON( DAEMON_ID(lr->sl_sd), wake );
+ break;
+ }
+ }
+ /* Walked the entire list without enabling anything; emfile
+ * counter is stale. Reset it.
+ */
+ if ( slap_listeners[i] == NULL ) emfile = 0;
+ }
+ ldap_pvt_thread_mutex_unlock( &slap_daemon[id].sd_mutex );
+ WAKE_DAEMON( id, wake || slapd_gentle_shutdown == 2 );
+}
+
+static void
+slapd_close( ber_socket_t s )
+{
+ Debug( LDAP_DEBUG_CONNS, "slapd_close: "
+ "closing %ld\n",
+ (long)s );
+ tcp_close( s );
+}
+
+static void
+slap_free_listener_addresses( struct sockaddr **sal )
+{
+ struct sockaddr **sap;
+ if ( sal == NULL ) return;
+ for ( sap = sal; *sap != NULL; sap++ )
+ ch_free(*sap);
+ ch_free( sal );
+}
+
+#if defined(LDAP_PF_LOCAL) || defined(SLAP_X_LISTENER_MOD)
+static int
+get_url_perms( char **exts, mode_t *perms, int *crit )
+{
+ int i;
+
+ assert( exts != NULL );
+ assert( perms != NULL );
+ assert( crit != NULL );
+
+ *crit = 0;
+ for ( i = 0; exts[i]; i++ ) {
+ char *type = exts[i];
+ int c = 0;
+
+ if ( type[0] == '!' ) {
+ c = 1;
+ type++;
+ }
+
+ if ( strncasecmp( type, LDAPI_MOD_URLEXT "=",
+ sizeof(LDAPI_MOD_URLEXT "=") - 1 ) == 0 ) {
+ char *value = type + ( sizeof(LDAPI_MOD_URLEXT "=") - 1 );
+ mode_t p = 0;
+ int j;
+
+ switch ( strlen( value ) ) {
+ case 4:
+ /* skip leading '0' */
+ if ( value[0] != '0' ) return LDAP_OTHER;
+ value++;
+
+ case 3:
+ for ( j = 0; j < 3; j++ ) {
+ int v;
+
+ v = value[j] - '0';
+
+ if ( v < 0 || v > 7 ) return LDAP_OTHER;
+
+ p |= v << 3 * ( 2 - j );
+ }
+ break;
+
+ case 10:
+ for ( j = 1; j < 10; j++ ) {
+ static mode_t m[] = { 0, S_IRUSR, S_IWUSR, S_IXUSR,
+ S_IRGRP, S_IWGRP, S_IXGRP, S_IROTH, S_IWOTH,
+ S_IXOTH };
+ static const char c[] = "-rwxrwxrwx";
+
+ if ( value[j] == c[j] ) {
+ p |= m[j];
+
+ } else if ( value[j] != '-' ) {
+ return LDAP_OTHER;
+ }
+ }
+ break;
+
+ default:
+ return LDAP_OTHER;
+ }
+
+ *crit = c;
+ *perms = p;
+
+ return LDAP_SUCCESS;
+ }
+ }
+
+ return LDAP_OTHER;
+}
+#endif /* LDAP_PF_LOCAL || SLAP_X_LISTENER_MOD */
+
+/* port = 0 indicates AF_LOCAL */
+static int
+slap_get_listener_addresses(
+ const char *host,
+ unsigned short port,
+ struct sockaddr ***sal )
+{
+ struct sockaddr **sap;
+
+#ifdef LDAP_PF_LOCAL
+ if ( port == 0 ) {
+ sap = *sal = ch_malloc( 2 * sizeof(void *) );
+
+ *sap = ch_calloc( 1, sizeof(struct sockaddr_un) );
+ sap[1] = NULL;
+
+ if ( strlen( host ) >
+ ( sizeof( ((struct sockaddr_un *)*sap)->sun_path ) - 1 ) ) {
+ Debug( LDAP_DEBUG_ANY, "slap_get_listener_addresses: "
+ "domain socket path (%s) too long in URL\n",
+ host );
+ goto errexit;
+ }
+
+ (*sap)->sa_family = AF_LOCAL;
+ strcpy( ((struct sockaddr_un *)*sap)->sun_path, host );
+ } else
+#endif /* LDAP_PF_LOCAL */
+ {
+#ifdef HAVE_GETADDRINFO
+ struct addrinfo hints, *res, *sai;
+ int n, err;
+ char serv[7];
+
+ memset( &hints, '\0', sizeof(hints) );
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_family = slap_inet4or6;
+ snprintf( serv, sizeof(serv), "%d", port );
+
+ if ( (err = getaddrinfo( host, serv, &hints, &res )) ) {
+ Debug( LDAP_DEBUG_ANY, "slap_get_listener_addresses: "
+ "getaddrinfo() failed: %s\n",
+ AC_GAI_STRERROR(err) );
+ return -1;
+ }
+
+ sai = res;
+ for ( n = 2; ( sai = sai->ai_next ) != NULL; n++ ) {
+ /* EMPTY */;
+ }
+ sap = *sal = ch_calloc( n, sizeof(void *) );
+
+ *sap = NULL;
+
+ for ( sai = res; sai; sai = sai->ai_next ) {
+ if ( sai->ai_addr == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "slap_get_listener_addresses: "
+ "getaddrinfo ai_addr is NULL?\n" );
+ freeaddrinfo( res );
+ goto errexit;
+ }
+
+ switch ( sai->ai_family ) {
+#ifdef LDAP_PF_INET6
+ case AF_INET6:
+ *sap = ch_malloc( sizeof(struct sockaddr_in6) );
+ *(struct sockaddr_in6 *)*sap =
+ *((struct sockaddr_in6 *)sai->ai_addr);
+ break;
+#endif /* LDAP_PF_INET6 */
+ case AF_INET:
+ *sap = ch_malloc( sizeof(struct sockaddr_in) );
+ *(struct sockaddr_in *)*sap =
+ *((struct sockaddr_in *)sai->ai_addr);
+ break;
+ default:
+ *sap = NULL;
+ break;
+ }
+
+ if ( *sap != NULL ) {
+ (*sap)->sa_family = sai->ai_family;
+ sap++;
+ *sap = NULL;
+ }
+ }
+
+ freeaddrinfo( res );
+
+#else /* ! HAVE_GETADDRINFO */
+ int i, n = 1;
+ struct in_addr in;
+ struct hostent *he = NULL;
+
+ if ( host == NULL ) {
+ in.s_addr = htonl( INADDR_ANY );
+
+ } else if ( !inet_aton( host, &in ) ) {
+ he = gethostbyname( host );
+ if ( he == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "slap_get_listener_addresses: "
+ "invalid host %s\n",
+ host );
+ return -1;
+ }
+ for ( n = 0; he->h_addr_list[n]; n++ ) /* empty */;
+ }
+
+ sap = *sal = ch_malloc( ( n + 1 ) * sizeof(void *) );
+
+ for ( i = 0; i < n; i++ ) {
+ sap[i] = ch_calloc( 1, sizeof(struct sockaddr_in) );
+ sap[i]->sa_family = AF_INET;
+ ((struct sockaddr_in *)sap[i])->sin_port = htons( port );
+ AC_MEMCPY( &((struct sockaddr_in *)sap[i])->sin_addr,
+ he ? (struct in_addr *)he->h_addr_list[i] : &in,
+ sizeof(struct in_addr) );
+ }
+ sap[i] = NULL;
+#endif /* ! HAVE_GETADDRINFO */
+ }
+
+ return 0;
+
+errexit:
+ slap_free_listener_addresses(*sal);
+ return -1;
+}
+
+static int
+slap_open_listener( const char *url, int *listeners, int *cur )
+{
+ int num, tmp, rc;
+ Listener l;
+ Listener *li;
+ LDAPURLDesc *lud;
+ unsigned short port;
+ int err, addrlen = 0;
+ struct sockaddr **sal = NULL, **psal;
+ int socktype = SOCK_STREAM; /* default to COTS */
+ ber_socket_t s;
+ char ebuf[128];
+
+#if defined(LDAP_PF_LOCAL) || defined(SLAP_X_LISTENER_MOD)
+ /*
+ * use safe defaults
+ */
+ int crit = 1;
+#endif /* LDAP_PF_LOCAL || SLAP_X_LISTENER_MOD */
+
+ rc = ldap_url_parse( url, &lud );
+
+ if ( rc != LDAP_URL_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "slap_open_listener: "
+ "listen URL \"%s\" parse error=%d\n",
+ url, rc );
+ return rc;
+ }
+
+ l.sl_url.bv_val = NULL;
+ l.sl_mute = 0;
+ l.sl_busy = 0;
+
+#ifndef HAVE_TLS
+ if ( ldap_pvt_url_scheme2tls( lud->lud_scheme ) ) {
+ Debug( LDAP_DEBUG_ANY, "slap_open_listener: "
+ "TLS not supported (%s)\n",
+ url );
+ ldap_free_urldesc( lud );
+ return -1;
+ }
+
+ if ( !lud->lud_port ) lud->lud_port = LDAP_PORT;
+
+#else /* HAVE_TLS */
+ l.sl_is_tls = ldap_pvt_url_scheme2tls( lud->lud_scheme );
+
+ if ( !lud->lud_port ) {
+ lud->lud_port = l.sl_is_tls ? LDAPS_PORT : LDAP_PORT;
+ }
+#endif /* HAVE_TLS */
+
+#ifdef LDAP_TCP_BUFFER
+ l.sl_tcp_rmem = 0;
+ l.sl_tcp_wmem = 0;
+#endif /* LDAP_TCP_BUFFER */
+
+ port = (unsigned short)lud->lud_port;
+
+ tmp = ldap_pvt_url_scheme2proto( lud->lud_scheme );
+ if ( tmp == LDAP_PROTO_IPC ) {
+#ifdef LDAP_PF_LOCAL
+ if ( lud->lud_host == NULL || lud->lud_host[0] == '\0' ) {
+ err = slap_get_listener_addresses( LDAPI_SOCK, 0, &sal );
+ } else {
+ err = slap_get_listener_addresses( lud->lud_host, 0, &sal );
+ }
+#else /* ! LDAP_PF_LOCAL */
+
+ Debug( LDAP_DEBUG_ANY, "slap_open_listener: "
+ "URL scheme not supported: %s\n",
+ url );
+ ldap_free_urldesc( lud );
+ return -1;
+#endif /* ! LDAP_PF_LOCAL */
+ } else {
+ if ( lud->lud_host == NULL || lud->lud_host[0] == '\0' ||
+ strcmp( lud->lud_host, "*" ) == 0 ) {
+ err = slap_get_listener_addresses( NULL, port, &sal );
+ } else {
+ err = slap_get_listener_addresses( lud->lud_host, port, &sal );
+ }
+ }
+
+#if defined(LDAP_PF_LOCAL) || defined(SLAP_X_LISTENER_MOD)
+ if ( lud->lud_exts ) {
+ err = get_url_perms( lud->lud_exts, &l.sl_perms, &crit );
+ } else {
+ l.sl_perms = S_IRWXU | S_IRWXO;
+ }
+#endif /* LDAP_PF_LOCAL || SLAP_X_LISTENER_MOD */
+
+ ldap_free_urldesc( lud );
+ if ( err ) {
+ slap_free_listener_addresses( sal );
+ return -1;
+ }
+
+ /* If we got more than one address returned, we need to make space
+ * for it in the slap_listeners array.
+ */
+ for ( num = 0; sal[num]; num++ ) /* empty */;
+ if ( num > 1 ) {
+ *listeners += num - 1;
+ slap_listeners = ch_realloc(
+ slap_listeners, ( *listeners + 1 ) * sizeof(Listener *) );
+ }
+
+ psal = sal;
+ while ( *sal != NULL ) {
+ char *af;
+ switch ( (*sal)->sa_family ) {
+ case AF_INET:
+ af = "IPv4";
+ break;
+#ifdef LDAP_PF_INET6
+ case AF_INET6:
+ af = "IPv6";
+ break;
+#endif /* LDAP_PF_INET6 */
+#ifdef LDAP_PF_LOCAL
+ case AF_LOCAL:
+ af = "Local";
+ break;
+#endif /* LDAP_PF_LOCAL */
+ default:
+ sal++;
+ continue;
+ }
+
+ s = socket( (*sal)->sa_family, socktype, 0 );
+ if ( s == AC_SOCKET_INVALID ) {
+ int err = sock_errno();
+ Debug( LDAP_DEBUG_ANY, "slap_open_listener: "
+ "%s socket() failed errno=%d (%s)\n",
+ af, err, sock_errstr( err, ebuf, sizeof(ebuf) ) );
+ sal++;
+ continue;
+ }
+ ber_pvt_socket_set_nonblock( s, 1 );
+ l.sl_sd = s;
+
+#ifdef LDAP_PF_LOCAL
+ if ( (*sal)->sa_family == AF_LOCAL ) {
+ unlink( ((struct sockaddr_un *)*sal)->sun_path );
+ } else
+#endif /* LDAP_PF_LOCAL */
+ {
+#ifdef SO_REUSEADDR
+ /* enable address reuse */
+ tmp = 1;
+ rc = setsockopt(
+ s, SOL_SOCKET, SO_REUSEADDR, (char *)&tmp, sizeof(tmp) );
+ if ( rc == AC_SOCKET_ERROR ) {
+ int err = sock_errno();
+ Debug( LDAP_DEBUG_ANY, "slap_open_listener(%ld): "
+ "setsockopt(SO_REUSEADDR) failed errno=%d (%s)\n",
+ (long)l.sl_sd, err,
+ sock_errstr( err, ebuf, sizeof(ebuf) ) );
+ }
+#endif /* SO_REUSEADDR */
+ }
+
+ switch ( (*sal)->sa_family ) {
+ case AF_INET:
+ addrlen = sizeof(struct sockaddr_in);
+ break;
+#ifdef LDAP_PF_INET6
+ case AF_INET6:
+#ifdef IPV6_V6ONLY
+ /* Try to use IPv6 sockets for IPv6 only */
+ tmp = 1;
+ rc = setsockopt( s, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&tmp,
+ sizeof(tmp) );
+ if ( rc == AC_SOCKET_ERROR ) {
+ int err = sock_errno();
+ Debug( LDAP_DEBUG_ANY, "slap_open_listener(%ld): "
+ "setsockopt(IPV6_V6ONLY) failed errno=%d (%s)\n",
+ (long)l.sl_sd, err,
+ sock_errstr( err, ebuf, sizeof(ebuf) ) );
+ }
+#endif /* IPV6_V6ONLY */
+ addrlen = sizeof(struct sockaddr_in6);
+ break;
+#endif /* LDAP_PF_INET6 */
+
+#ifdef LDAP_PF_LOCAL
+ case AF_LOCAL:
+#ifdef LOCAL_CREDS
+ {
+ int one = 1;
+ setsockopt( s, 0, LOCAL_CREDS, &one, sizeof(one) );
+ }
+#endif /* LOCAL_CREDS */
+
+ addrlen = sizeof(struct sockaddr_un);
+ break;
+#endif /* LDAP_PF_LOCAL */
+ }
+
+#ifdef LDAP_PF_LOCAL
+ /* create socket with all permissions set for those systems
+ * that honor permissions on sockets (e.g. Linux); typically,
+ * only write is required. To exploit filesystem permissions,
+ * place the socket in a directory and use directory's
+ * permissions. Need write perms to the directory to
+ * create/unlink the socket; likely need exec perms to access
+ * the socket (ITS#4709) */
+ {
+ mode_t old_umask = 0;
+
+ if ( (*sal)->sa_family == AF_LOCAL ) {
+ old_umask = umask( 0 );
+ }
+#endif /* LDAP_PF_LOCAL */
+ rc = bind( s, *sal, addrlen );
+#ifdef LDAP_PF_LOCAL
+ if ( old_umask != 0 ) {
+ umask( old_umask );
+ }
+ }
+#endif /* LDAP_PF_LOCAL */
+ if ( rc ) {
+ err = sock_errno();
+ Debug( LDAP_DEBUG_ANY, "slap_open_listener: "
+ "bind(%ld) failed errno=%d (%s)\n",
+ (long)l.sl_sd, err,
+ sock_errstr( err, ebuf, sizeof(ebuf) ) );
+ tcp_close( s );
+ sal++;
+ continue;
+ }
+
+ switch ( (*sal)->sa_family ) {
+#ifdef LDAP_PF_LOCAL
+ case AF_LOCAL: {
+ char *path = ((struct sockaddr_un *)*sal)->sun_path;
+ l.sl_name.bv_len = strlen( path ) + STRLENOF("PATH=");
+ l.sl_name.bv_val = ch_malloc( l.sl_name.bv_len + 1 );
+ snprintf( l.sl_name.bv_val, l.sl_name.bv_len + 1, "PATH=%s",
+ path );
+ } break;
+#endif /* LDAP_PF_LOCAL */
+
+ case AF_INET: {
+ char addr[INET_ADDRSTRLEN];
+ const char *s;
+#if defined(HAVE_GETADDRINFO) && defined(HAVE_INET_NTOP)
+ s = inet_ntop( AF_INET,
+ &((struct sockaddr_in *)*sal)->sin_addr, addr,
+ sizeof(addr) );
+#else /* ! HAVE_GETADDRINFO || ! HAVE_INET_NTOP */
+ s = inet_ntoa( ((struct sockaddr_in *)*sal)->sin_addr );
+#endif /* ! HAVE_GETADDRINFO || ! HAVE_INET_NTOP */
+ if ( !s ) s = SLAP_STRING_UNKNOWN;
+ port = ntohs( ((struct sockaddr_in *)*sal)->sin_port );
+ l.sl_name.bv_val =
+ ch_malloc( sizeof("IP=255.255.255.255:65535") );
+ snprintf( l.sl_name.bv_val,
+ sizeof("IP=255.255.255.255:65535"), "IP=%s:%d", s,
+ port );
+ l.sl_name.bv_len = strlen( l.sl_name.bv_val );
+ } break;
+
+#ifdef LDAP_PF_INET6
+ case AF_INET6: {
+ char addr[INET6_ADDRSTRLEN];
+ const char *s;
+ s = inet_ntop( AF_INET6,
+ &((struct sockaddr_in6 *)*sal)->sin6_addr, addr,
+ sizeof(addr) );
+ if ( !s ) s = SLAP_STRING_UNKNOWN;
+ port = ntohs( ((struct sockaddr_in6 *)*sal)->sin6_port );
+ l.sl_name.bv_len = strlen( s ) + sizeof("IP=[]:65535");
+ l.sl_name.bv_val = ch_malloc( l.sl_name.bv_len );
+ snprintf( l.sl_name.bv_val, l.sl_name.bv_len, "IP=[%s]:%d", s,
+ port );
+ l.sl_name.bv_len = strlen( l.sl_name.bv_val );
+ } break;
+#endif /* LDAP_PF_INET6 */
+
+ default:
+ Debug( LDAP_DEBUG_ANY, "slap_open_listener: "
+ "unsupported address family (%d)\n",
+ (int)(*sal)->sa_family );
+ break;
+ }
+
+ AC_MEMCPY( &l.sl_sa, *sal, addrlen );
+ ber_str2bv( url, 0, 1, &l.sl_url );
+ li = ch_malloc( sizeof(Listener) );
+ *li = l;
+ slap_listeners[*cur] = li;
+ (*cur)++;
+ sal++;
+ }
+
+ slap_free_listener_addresses( psal );
+
+ if ( l.sl_url.bv_val == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "slap_open_listener: "
+ "failed on %s\n",
+ url );
+ return -1;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "slap_open_listener: "
+ "listener initialized %s\n",
+ l.sl_url.bv_val );
+
+ return 0;
+}
+
+int lloadd_inited = 0;
+
+int
+slapd_daemon_init( const char *urls )
+{
+ int i, j, n;
+ char **u;
+
+ Debug( LDAP_DEBUG_ARGS, "slapd_daemon_init: %s\n",
+ urls ? urls : "<null>" );
+
+#ifdef HAVE_TCPD
+ ldap_pvt_thread_mutex_init( &sd_tcpd_mutex );
+#endif /* TCP Wrappers */
+
+ if ( urls == NULL ) urls = "ldap:///";
+
+ u = ldap_str2charray( urls, " " );
+
+ if ( u == NULL || u[0] == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "slapd_daemon_init: "
+ "no urls (%s) provided.\n",
+ urls );
+ if ( u ) ldap_charray_free( u );
+ return -1;
+ }
+
+ for ( i = 0; u[i] != NULL; i++ ) {
+ Debug( LDAP_DEBUG_TRACE, "slapd_daemon_init: "
+ "listen on %s\n",
+ u[i] );
+ }
+
+ if ( i == 0 ) {
+ Debug( LDAP_DEBUG_ANY, "slapd_daemon_init: "
+ "no listeners to open (%s)\n",
+ urls );
+ ldap_charray_free( u );
+ return -1;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "slapd_daemon_init: "
+ "%d listeners to open...\n",
+ i );
+ slap_listeners = ch_malloc( ( i + 1 ) * sizeof(Listener *) );
+
+ for ( n = 0, j = 0; u[n]; n++ ) {
+ if ( slap_open_listener( u[n], &i, &j ) ) {
+ ldap_charray_free( u );
+ return -1;
+ }
+ }
+ slap_listeners[j] = NULL;
+
+ Debug( LDAP_DEBUG_TRACE, "slapd_daemon_init: "
+ "%d listeners opened\n",
+ i );
+
+ ldap_charray_free( u );
+
+ return !i;
+}
+
+int
+slapd_daemon_destroy( void )
+{
+ if ( lloadd_inited ) {
+ int i;
+
+ for ( i = 0; i < slapd_daemon_threads; i++ ) {
+ ldap_pvt_thread_mutex_destroy( &slap_daemon[i].sd_mutex );
+ }
+ lloadd_inited = 0;
+#ifdef HAVE_TCPD
+ ldap_pvt_thread_mutex_destroy( &sd_tcpd_mutex );
+#endif /* TCP Wrappers */
+ }
+
+ return 0;
+}
+
+static void
+close_listeners( int remove )
+{
+ int l;
+
+ if ( !listening ) return;
+ listening = 0;
+
+ for ( l = 0; slap_listeners[l] != NULL; l++ ) {
+ Listener *lr = slap_listeners[l];
+
+ if ( lr->sl_sd != AC_SOCKET_INVALID ) {
+ int s = lr->sl_sd;
+ lr->sl_sd = AC_SOCKET_INVALID;
+ if ( remove ) slapd_remove( s, NULL, 0, 0, 0 );
+
+#ifdef LDAP_PF_LOCAL
+ if ( lr->sl_sa.sa_addr.sa_family == AF_LOCAL ) {
+ unlink( lr->sl_sa.sa_un_addr.sun_path );
+ }
+#endif /* LDAP_PF_LOCAL */
+
+ slapd_close( s );
+ }
+ }
+}
+
+static void
+destroy_listeners( void )
+{
+ Listener *lr, **ll = slap_listeners;
+
+ if ( ll == NULL ) return;
+
+ ldap_pvt_thread_join( listener_tid, (void *)NULL );
+
+ while ( (lr = *ll++) != NULL ) {
+ if ( lr->sl_url.bv_val ) {
+ ber_memfree( lr->sl_url.bv_val );
+ }
+
+ if ( lr->sl_name.bv_val ) {
+ ber_memfree( lr->sl_name.bv_val );
+ }
+
+ evconnlistener_free( lr->listener );
+
+ free( lr );
+ }
+
+ free( slap_listeners );
+ slap_listeners = NULL;
+}
+
+static void
+slap_listener(
+ struct evconnlistener *listener,
+ ber_socket_t s,
+ struct sockaddr *a,
+ int len,
+ void *arg )
+{
+ Listener *sl = arg;
+ Connection *c;
+ Sockaddr *from = (Sockaddr *)a;
+#ifdef SLAPD_RLOOKUPS
+ char hbuf[NI_MAXHOST];
+#endif /* SLAPD_RLOOKUPS */
+
+ const char *peeraddr = NULL;
+ /* we assume INET6_ADDRSTRLEN > INET_ADDRSTRLEN */
+ char addr[INET6_ADDRSTRLEN];
+#ifdef LDAP_PF_LOCAL
+ char peername[MAXPATHLEN + sizeof("PATH=")];
+#ifdef LDAP_PF_LOCAL_SENDMSG
+ char peerbuf[8];
+ struct berval peerbv = BER_BVNULL;
+#endif
+#elif defined(LDAP_PF_INET6)
+ char peername[sizeof("IP=[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535")];
+#else /* ! LDAP_PF_LOCAL && ! LDAP_PF_INET6 */
+ char peername[sizeof("IP=255.255.255.255:65336")];
+#endif /* LDAP_PF_LOCAL */
+ int cflag;
+ int tid;
+ char ebuf[128];
+
+ Debug( LDAP_DEBUG_TRACE, ">>> slap_listener(%s)\n", sl->sl_url.bv_val );
+
+ peername[0] = '\0';
+
+ /* Resume the listener FD to allow concurrent-processing of
+ * additional incoming connections.
+ */
+ sl->sl_busy = 0;
+
+ tid = DAEMON_ID(s);
+
+ Debug( LDAP_DEBUG_CONNS, "slap_listener: "
+ "listen=%ld, new connection fd=%ld\n",
+ (long)sl->sl_sd, (long)s );
+
+#if defined(SO_KEEPALIVE) || defined(TCP_NODELAY)
+#ifdef LDAP_PF_LOCAL
+ /* for IPv4 and IPv6 sockets only */
+ if ( from->sa_addr.sa_family != AF_LOCAL )
+#endif /* LDAP_PF_LOCAL */
+ {
+ int rc;
+ int tmp;
+#ifdef SO_KEEPALIVE
+ /* enable keep alives */
+ tmp = 1;
+ rc = setsockopt(
+ s, SOL_SOCKET, SO_KEEPALIVE, (char *)&tmp, sizeof(tmp) );
+ if ( rc == AC_SOCKET_ERROR ) {
+ int err = sock_errno();
+ Debug( LDAP_DEBUG_ANY, "slap_listener(%ld): "
+ "setsockopt(SO_KEEPALIVE) failed errno=%d (%s)\n",
+ (long)s, err, sock_errstr( err, ebuf, sizeof(ebuf) ) );
+ }
+#endif /* SO_KEEPALIVE */
+#ifdef TCP_NODELAY
+ /* enable no delay */
+ tmp = 1;
+ rc = setsockopt(
+ s, IPPROTO_TCP, TCP_NODELAY, (char *)&tmp, sizeof(tmp) );
+ if ( rc == AC_SOCKET_ERROR ) {
+ int err = sock_errno();
+ Debug( LDAP_DEBUG_ANY, "slap_listener(%ld): "
+ "setsockopt(TCP_NODELAY) failed errno=%d (%s)\n",
+ (long)s, err, sock_errstr( err, ebuf, sizeof(ebuf) ) );
+ }
+#endif /* TCP_NODELAY */
+ }
+#endif /* SO_KEEPALIVE || TCP_NODELAY */
+
+ cflag = 0;
+ switch ( from->sa_addr.sa_family ) {
+#ifdef LDAP_PF_LOCAL
+ case AF_LOCAL:
+ cflag |= CONN_IS_IPC;
+
+ /* FIXME: apparently accept doesn't fill the sun_path member */
+ sprintf( peername, "PATH=%s", sl->sl_sa.sa_un_addr.sun_path );
+ break;
+#endif /* LDAP_PF_LOCAL */
+
+#ifdef LDAP_PF_INET6
+ case AF_INET6:
+ if ( IN6_IS_ADDR_V4MAPPED( &from->sa_in6_addr.sin6_addr ) ) {
+#if defined(HAVE_GETADDRINFO) && defined(HAVE_INET_NTOP)
+ peeraddr = inet_ntop( AF_INET,
+ ( (struct in_addr *)&from->sa_in6_addr.sin6_addr
+ .s6_addr[12] ),
+ addr, sizeof(addr) );
+#else /* ! HAVE_GETADDRINFO || ! HAVE_INET_NTOP */
+ peeraddr = inet_ntoa( *( (struct in_addr *)&from->sa_in6_addr
+ .sin6_addr.s6_addr[12] ) );
+#endif /* ! HAVE_GETADDRINFO || ! HAVE_INET_NTOP */
+ if ( !peeraddr ) peeraddr = SLAP_STRING_UNKNOWN;
+ sprintf( peername, "IP=%s:%d", peeraddr,
+ (unsigned)ntohs( from->sa_in6_addr.sin6_port ) );
+ } else {
+ peeraddr = inet_ntop( AF_INET6, &from->sa_in6_addr.sin6_addr,
+ addr, sizeof(addr) );
+ if ( !peeraddr ) peeraddr = SLAP_STRING_UNKNOWN;
+ sprintf( peername, "IP=[%s]:%d", peeraddr,
+ (unsigned)ntohs( from->sa_in6_addr.sin6_port ) );
+ }
+ break;
+#endif /* LDAP_PF_INET6 */
+
+ case AF_INET: {
+#if defined(HAVE_GETADDRINFO) && defined(HAVE_INET_NTOP)
+ peeraddr = inet_ntop(
+ AF_INET, &from->sa_in_addr.sin_addr, addr, sizeof(addr) );
+#else /* ! HAVE_GETADDRINFO || ! HAVE_INET_NTOP */
+ peeraddr = inet_ntoa( from->sa_in_addr.sin_addr );
+#endif /* ! HAVE_GETADDRINFO || ! HAVE_INET_NTOP */
+ if ( !peeraddr ) peeraddr = SLAP_STRING_UNKNOWN;
+ sprintf( peername, "IP=%s:%d", peeraddr,
+ (unsigned)ntohs( from->sa_in_addr.sin_port ) );
+ } break;
+
+ default:
+ slapd_close( s );
+ return;
+ }
+
+#ifdef HAVE_TLS
+ if ( sl->sl_is_tls ) cflag |= CONN_IS_TLS;
+#endif
+ c = client_init( s, sl, peername, slap_daemon[tid].base, cflag );
+
+ if ( !c ) {
+ Debug( LDAP_DEBUG_ANY, "slap_listener: "
+ "client_init(%ld, %s, %s) failed.\n",
+ (long)s, peername, sl->sl_name.bv_val );
+ slapd_close( s );
+ }
+
+ return;
+}
+
+static void *
+slap_listener_thread( void *ctx )
+{
+ int rc = event_base_dispatch( listener_base );
+ Debug( LDAP_DEBUG_ANY, "slap_listener_thread: "
+ "event loop finished: rc=%d\n",
+ rc );
+
+ return (void *)NULL;
+}
+
+static void
+listener_error_cb( struct evconnlistener *lev, void *arg )
+{
+ Listener *l = arg;
+ int err = EVUTIL_SOCKET_ERROR();
+
+ assert( l->listener == lev );
+ if (
+#ifdef EMFILE
+ err == EMFILE ||
+#endif /* EMFILE */
+#ifdef ENFILE
+ err == ENFILE ||
+#endif /* ENFILE */
+ 0 ) {
+ ldap_pvt_thread_mutex_lock( &slap_daemon[0].sd_mutex );
+ emfile++;
+ /* Stop listening until an existing session closes */
+ l->sl_mute = 1;
+ evconnlistener_disable( lev );
+ ldap_pvt_thread_mutex_unlock( &slap_daemon[0].sd_mutex );
+ } else {
+ char ebuf[128];
+ Debug( LDAP_DEBUG_ANY, "listener_error_cb: "
+ "received an error on a listener, shutting down: '%s'\n",
+ sock_errstr( err, ebuf, sizeof(ebuf) ) );
+ event_base_loopexit( l->base, NULL );
+ }
+}
+
+static int
+slap_listener_activate( void )
+{
+ struct evconnlistener *listener;
+ int l, rc;
+ char ebuf[128];
+
+ listener_base = event_base_new();
+ if ( !listener_base ) return -1;
+
+ for ( l = 0; slap_listeners[l] != NULL; l++ ) {
+ if ( slap_listeners[l]->sl_sd == AC_SOCKET_INVALID ) continue;
+
+ /* FIXME: TCP-only! */
+#ifdef LDAP_TCP_BUFFER
+ if ( 1 ) {
+ int origsize, size, realsize, rc;
+ socklen_t optlen;
+
+ size = 0;
+ if ( slap_listeners[l]->sl_tcp_rmem > 0 ) {
+ size = slap_listeners[l]->sl_tcp_rmem;
+ } else if ( slapd_tcp_rmem > 0 ) {
+ size = slapd_tcp_rmem;
+ }
+
+ if ( size > 0 ) {
+ optlen = sizeof(origsize);
+ rc = getsockopt( slap_listeners[l]->sl_sd, SOL_SOCKET,
+ SO_RCVBUF, (void *)&origsize, &optlen );
+
+ if ( rc ) {
+ int err = sock_errno();
+ Debug( LDAP_DEBUG_ANY, "slap_listener_activate: "
+ "getsockopt(SO_RCVBUF) failed errno=%d (%s)\n",
+ err, AC_STRERROR_R( err, ebuf, sizeof(ebuf) ) );
+ }
+
+ optlen = sizeof(size);
+ rc = setsockopt( slap_listeners[l]->sl_sd, SOL_SOCKET,
+ SO_RCVBUF, (const void *)&size, optlen );
+
+ if ( rc ) {
+ int err = sock_errno();
+ Debug( LDAP_DEBUG_ANY, "slapd_listener_activate: "
+ "setsockopt(SO_RCVBUF) failed errno=%d (%s)\n",
+ err, sock_errstr( err, ebuf, sizeof(ebuf) ) );
+ }
+
+ optlen = sizeof(realsize);
+ rc = getsockopt( slap_listeners[l]->sl_sd, SOL_SOCKET,
+ SO_RCVBUF, (void *)&realsize, &optlen );
+
+ if ( rc ) {
+ int err = sock_errno();
+ Debug( LDAP_DEBUG_ANY, "slapd_listener_activate: "
+ "getsockopt(SO_RCVBUF) failed errno=%d (%s)\n",
+ err, sock_errstr( err, ebuf, sizeof(ebuf) ) );
+ }
+
+ Debug( LDAP_DEBUG_ANY, "slapd_listener_activate: "
+ "url=%s (#%d) RCVBUF original size=%d requested "
+ "size=%d real size=%d\n",
+ slap_listeners[l]->sl_url.bv_val, l, origsize, size,
+ realsize );
+ }
+
+ size = 0;
+ if ( slap_listeners[l]->sl_tcp_wmem > 0 ) {
+ size = slap_listeners[l]->sl_tcp_wmem;
+ } else if ( slapd_tcp_wmem > 0 ) {
+ size = slapd_tcp_wmem;
+ }
+
+ if ( size > 0 ) {
+ optlen = sizeof(origsize);
+ rc = getsockopt( slap_listeners[l]->sl_sd, SOL_SOCKET,
+ SO_SNDBUF, (void *)&origsize, &optlen );
+
+ if ( rc ) {
+ int err = sock_errno();
+ Debug( LDAP_DEBUG_ANY, "slapd_listener_activate: "
+ "getsockopt(SO_SNDBUF) failed errno=%d (%s)\n",
+ err, sock_errstr( err, ebuf, sizeof(ebuf) ) );
+ }
+
+ optlen = sizeof(size);
+ rc = setsockopt( slap_listeners[l]->sl_sd, SOL_SOCKET,
+ SO_SNDBUF, (const void *)&size, optlen );
+
+ if ( rc ) {
+ int err = sock_errno();
+ Debug( LDAP_DEBUG_ANY, "slapd_listener_activate: "
+ "setsockopt(SO_SNDBUF) failed errno=%d (%s)\n",
+ err, sock_errstr( err, ebuf, sizeof(ebuf) ) );
+ }
+
+ optlen = sizeof(realsize);
+ rc = getsockopt( slap_listeners[l]->sl_sd, SOL_SOCKET,
+ SO_SNDBUF, (void *)&realsize, &optlen );
+
+ if ( rc ) {
+ int err = sock_errno();
+ Debug( LDAP_DEBUG_ANY, "slapd_listener_activate: "
+ "getsockopt(SO_SNDBUF) failed errno=%d (%s)\n",
+ err, sock_errstr( err, ebuf, sizeof(ebuf) ) );
+ }
+
+ Debug( LDAP_DEBUG_ANY, "slapd_listener_activate: "
+ "url=%s (#%d) SNDBUF original size=%d requested "
+ "size=%d real size=%d\n",
+ slap_listeners[l]->sl_url.bv_val, l, origsize, size,
+ realsize );
+ }
+ }
+#endif /* LDAP_TCP_BUFFER */
+
+ slap_listeners[l]->sl_busy = 1;
+ listener = evconnlistener_new( listener_base, slap_listener,
+ slap_listeners[l], LEV_OPT_THREADSAFE, SLAPD_LISTEN_BACKLOG,
+ slap_listeners[l]->sl_sd );
+ if ( !listener ) {
+ int err = sock_errno();
+
+#ifdef LDAP_PF_INET6
+ /* If error is EADDRINUSE, we are trying to listen to INADDR_ANY and
+ * we are already listening to in6addr_any, then we want to ignore
+ * this and continue.
+ */
+ if ( err == EADDRINUSE ) {
+ int i;
+ struct sockaddr_in sa = slap_listeners[l]->sl_sa.sa_in_addr;
+ struct sockaddr_in6 sa6;
+
+ if ( sa.sin_family == AF_INET &&
+ sa.sin_addr.s_addr == htonl( INADDR_ANY ) ) {
+ for ( i = 0; i < l; i++ ) {
+ sa6 = slap_listeners[i]->sl_sa.sa_in6_addr;
+ if ( sa6.sin6_family == AF_INET6 &&
+ !memcmp( &sa6.sin6_addr, &in6addr_any,
+ sizeof(struct in6_addr) ) ) {
+ break;
+ }
+ }
+
+ if ( i < l ) {
+ /* We are already listening to in6addr_any */
+ Debug( LDAP_DEBUG_CONNS, "slap_listener_activate: "
+ "Attempt to listen to 0.0.0.0 failed, "
+ "already listening on ::, assuming IPv4 "
+ "included\n" );
+ slapd_close( slap_listeners[l]->sl_sd );
+ slap_listeners[l]->sl_sd = AC_SOCKET_INVALID;
+ continue;
+ }
+ }
+ }
+#endif /* LDAP_PF_INET6 */
+ Debug( LDAP_DEBUG_ANY, "slap_listener_activate: "
+ "listen(%s, 5) failed errno=%d (%s)\n",
+ slap_listeners[l]->sl_url.bv_val, err,
+ sock_errstr( err, ebuf, sizeof(ebuf) ) );
+ return -1;
+ }
+
+ slap_listeners[l]->base = listener_base;
+ slap_listeners[l]->listener = listener;
+ evconnlistener_set_error_cb( listener, listener_error_cb );
+ }
+
+ rc = ldap_pvt_thread_create(
+ &listener_tid, 0, slap_listener_thread, slap_listeners[l] );
+
+ if ( rc != 0 ) {
+ Debug( LDAP_DEBUG_ANY, "slap_listener_activate(%d): "
+ "submit failed (%d)\n",
+ slap_listeners[l]->sl_sd, rc );
+ }
+ return rc;
+}
+
+static void *
+slapd_daemon_task( void *ptr )
+{
+ int rc;
+ int tid = (ldap_pvt_thread_t *)ptr - daemon_tid;
+ struct event_base *base;
+ struct event *event;
+
+ base = event_base_new();
+ if ( !base ) {
+ Debug( LDAP_DEBUG_ANY, "slapd_daemon_task: "
+ "failed to acquire event base\n" );
+ return (void *)-1;
+ }
+ slap_daemon[tid].base = base;
+
+ event = event_new( base, -1, EV_WRITE, daemon_wakeup_cb, ptr );
+ if ( !event ) {
+ Debug( LDAP_DEBUG_ANY, "slapd_daemon_task: "
+ "failed to set up the wakeup event\n" );
+ return (void *)-1;
+ }
+ event_add( event, NULL );
+ slap_daemon[tid].wakeup_event = event;
+
+ /* run */
+ rc = event_base_dispatch( base );
+ Debug( LDAP_DEBUG_ANY, "slapd_daemon_task: "
+ "Daemon %d, event loop finished: rc=%d\n",
+ tid, rc );
+
+ if ( !slapd_gentle_shutdown ) {
+ slapd_abrupt_shutdown = 1;
+ }
+
+ return NULL;
+}
+
+int
+slapd_daemon( struct event_base *daemon_base )
+{
+ int i, rc;
+
+ assert( daemon_base != NULL );
+
+ if ( slapd_daemon_threads > SLAPD_MAX_DAEMON_THREADS )
+ slapd_daemon_threads = SLAPD_MAX_DAEMON_THREADS;
+
+ daemon_tid =
+ ch_malloc( slapd_daemon_threads * sizeof(ldap_pvt_thread_t) );
+
+ if ( (rc = slap_listener_activate()) != 0 ) {
+ return rc;
+ }
+
+ for ( i = 0; i < slapd_daemon_threads; i++ ) {
+ ldap_pvt_thread_mutex_init( &slap_daemon[i].sd_mutex );
+ /* threads that handle client and upstream sockets */
+ rc = ldap_pvt_thread_create(
+ &daemon_tid[i], 0, slapd_daemon_task, &daemon_tid[i] );
+
+ if ( rc != 0 ) {
+ Debug( LDAP_DEBUG_ANY, "lloadd startup: "
+ "listener ldap_pvt_thread_create failed (%d)\n",
+ rc );
+ return rc;
+ }
+ }
+
+ lloadd_inited = 1;
+ rc = event_base_dispatch( daemon_base );
+ Debug( LDAP_DEBUG_ANY, "lloadd shutdown: "
+ "Main event loop finished: rc=%d\n",
+ rc );
+
+ /* shutdown */
+ event_base_loopexit( listener_base, 0 );
+ close_listeners( 0 );
+
+ /* wait for the listener threads to complete */
+ destroy_listeners();
+
+ for ( i = 0; i < slapd_daemon_threads; i++ )
+ ldap_pvt_thread_join( daemon_tid[i], (void *)NULL );
+
+ if ( LogTest( LDAP_DEBUG_ANY ) ) {
+ int t = ldap_pvt_thread_pool_backload( &connection_pool );
+ Debug( LDAP_DEBUG_ANY, "lloadd shutdown: "
+ "waiting for %d operations/tasks to finish\n",
+ t );
+ }
+ ldap_pvt_thread_pool_close( &connection_pool, 1 );
+
+ ch_free( daemon_tid );
+ daemon_tid = NULL;
+
+ slapd_daemon_destroy();
+
+ return 0;
+}
+
+static void
+daemon_wakeup_cb( evutil_socket_t sig, short what, void *arg )
+{
+ int tid = (ldap_pvt_thread_t *)arg - daemon_tid;
+
+ Debug( LDAP_DEBUG_TRACE, "daemon_wakeup_cb: "
+ "Daemon thread %d woken up\n",
+ tid );
+ if ( slapd_shutdown ) {
+ event_base_loopexit( slap_daemon[tid].base, NULL );
+ }
+}
+
+void
+slap_sig_shutdown( evutil_socket_t sig, short what, void *arg )
+{
+ struct event_base *daemon_base = arg;
+ int save_errno = errno;
+ int i;
+
+ /*
+ * If the NT Service Manager is controlling the server, we don't
+ * want SIGBREAK to kill the server. For some strange reason,
+ * SIGBREAK is generated when a user logs out.
+ */
+
+#if defined(HAVE_NT_SERVICE_MANAGER) && defined(SIGBREAK)
+ if ( is_NT_Service && sig == SIGBREAK ) {
+ /* empty */;
+ } else
+#endif /* HAVE_NT_SERVICE_MANAGER && SIGBREAK */
+#ifdef SIGHUP
+ if ( sig == SIGHUP && global_gentlehup && slapd_gentle_shutdown == 0 ) {
+ slapd_gentle_shutdown = 1;
+ } else
+#endif /* SIGHUP */
+ {
+ slapd_shutdown = 1;
+ }
+
+ for ( i = 0; i < slapd_daemon_threads; i++ ) {
+ WAKE_DAEMON( i, 1 );
+ }
+ event_base_loopexit( daemon_base, NULL );
+
+ errno = save_errno;
+}
+
+Listener **
+slapd_get_listeners( void )
+{
+ /* Could return array with no listeners if !listening, but current
+ * callers mostly look at the URLs. E.g. syncrepl uses this to
+ * identify the server, which means it wants the startup arguments.
+ */
+ return slap_listeners;
+}
+
+/* Reject all incoming requests */
+void
+slap_suspend_listeners( void )
+{
+ int i;
+ for ( i = 0; slap_listeners[i]; i++ ) {
+ slap_listeners[i]->sl_mute = 1;
+ evconnlistener_disable( slap_listeners[i]->listener );
+ listen( slap_listeners[i]->sl_sd, 0 );
+ }
+}
+
+/* Resume after a suspend */
+void
+slap_resume_listeners( void )
+{
+ int i;
+ for ( i = 0; slap_listeners[i]; i++ ) {
+ slap_listeners[i]->sl_mute = 0;
+ listen( slap_listeners[i]->sl_sd, SLAPD_LISTEN_BACKLOG );
+ evconnlistener_enable( slap_listeners[i]->listener );
+ }
+}
--- /dev/null
+../../doc/devel/lloadd/design.md
\ No newline at end of file
--- /dev/null
+../slapd/globals.c
\ No newline at end of file
--- /dev/null
+/* init.c - initialize various things */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2020 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "slap.h"
+#include "lber_pvt.h"
+
+#include "ldap_rq.h"
+
+/*
+ * read-only global variables or variables only written by the listener
+ * thread (after they are initialized) - no need to protect them with a mutex.
+ */
+int slap_debug = 0;
+
+#ifdef LDAP_DEBUG
+int ldap_syslog = LDAP_DEBUG_STATS;
+#else
+int ldap_syslog;
+#endif
+
+#ifdef LOG_DEBUG
+int ldap_syslog_level = LOG_DEBUG;
+#endif
+
+/*
+ * global variables that need mutex protection
+ */
+ldap_pvt_thread_pool_t connection_pool;
+int connection_pool_max = SLAP_MAX_WORKER_THREADS;
+int connection_pool_queues = 1;
+int slap_tool_thread_max = 1;
+
+static const char *slap_name = NULL;
+int slapMode = SLAP_UNDEFINED_MODE;
+
+int
+slap_init( int mode, const char *name )
+{
+ int rc = LDAP_SUCCESS;
+
+ assert( mode );
+
+ if ( slapMode != SLAP_UNDEFINED_MODE ) {
+ /* Make sure we write something to stderr */
+ slap_debug |= LDAP_DEBUG_NONE;
+ Debug( LDAP_DEBUG_ANY, "%s init: "
+ "init called twice (old=%d, new=%d)\n",
+ name, slapMode, mode );
+
+ return 1;
+ }
+
+ slapMode = mode;
+
+ switch ( slapMode & SLAP_MODE ) {
+ case SLAP_SERVER_MODE:
+ Debug( LDAP_DEBUG_TRACE, "%s init: "
+ "initiated server.\n",
+ name );
+
+ slap_name = name;
+
+ ldap_pvt_thread_pool_init_q( &connection_pool, connection_pool_max,
+ 0, connection_pool_queues );
+
+ ldap_pvt_thread_mutex_init( &slapd_rq.rq_mutex );
+ LDAP_STAILQ_INIT( &slapd_rq.task_list );
+ LDAP_STAILQ_INIT( &slapd_rq.run_list );
+
+ break;
+
+ default:
+ slap_debug |= LDAP_DEBUG_NONE;
+ Debug( LDAP_DEBUG_ANY, "%s init: "
+ "undefined mode (%d).\n",
+ name, mode );
+
+ rc = 1;
+ break;
+ }
+
+ return rc;
+}
+
+int
+slap_destroy( void )
+{
+ int rc = LDAP_SUCCESS;
+
+ Debug( LDAP_DEBUG_TRACE, "%s destroy: "
+ "freeing system resources.\n",
+ slap_name );
+
+ ldap_pvt_thread_pool_free( &connection_pool );
+
+ switch ( slapMode & SLAP_MODE ) {
+ case SLAP_SERVER_MODE:
+ break;
+
+ default:
+ Debug( LDAP_DEBUG_ANY, "slap_destroy(): "
+ "undefined mode (%d).\n",
+ slapMode );
+
+ rc = 1;
+ break;
+ }
+
+ ldap_pvt_thread_destroy();
+
+ /* should destroy the above mutex */
+ return rc;
+}
--- /dev/null
+/* libevent_support.c - routines to bridge libldap and libevent */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2017-2020 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <ac/time.h>
+
+#include <event2/event.h>
+#include <event2/thread.h>
+
+#include "slap.h"
+#include "ldap_pvt_thread.h"
+
+static void *
+lload_libevent_mutex_init( unsigned locktype )
+{
+ int rc;
+ ldap_pvt_thread_mutex_t *mutex =
+ ch_malloc( sizeof(ldap_pvt_thread_mutex_t) );
+
+ if ( locktype & EVTHREAD_LOCKTYPE_RECURSIVE ) {
+ rc = ldap_pvt_thread_mutex_recursive_init( mutex );
+ } else {
+ rc = ldap_pvt_thread_mutex_init( mutex );
+ }
+ if ( rc ) {
+ ch_free( mutex );
+ mutex = NULL;
+ }
+ return mutex;
+}
+
+static void
+lload_libevent_mutex_destroy( void *lock, unsigned locktype )
+{
+ int rc;
+ ldap_pvt_thread_mutex_t *mutex = lock;
+
+ rc = ldap_pvt_thread_mutex_destroy( mutex );
+ assert( rc == 0 );
+ ch_free( mutex );
+}
+
+static int
+lload_libevent_mutex_lock( unsigned mode, void *lock )
+{
+ ldap_pvt_thread_mutex_t *mutex = lock;
+
+ if ( mode & EVTHREAD_TRY ) {
+ return ldap_pvt_thread_mutex_trylock( mutex );
+ } else {
+ return ldap_pvt_thread_mutex_lock( mutex );
+ }
+}
+
+static int
+lload_libevent_mutex_unlock( unsigned mode, void *lock )
+{
+ ldap_pvt_thread_mutex_t *mutex = lock;
+
+ return ldap_pvt_thread_mutex_unlock( mutex );
+}
+
+static void *
+lload_libevent_cond_init( unsigned condtype )
+{
+ int rc;
+ ldap_pvt_thread_cond_t *cond =
+ ch_malloc( sizeof(ldap_pvt_thread_cond_t) );
+
+ assert( condtype == 0 );
+ rc = ldap_pvt_thread_cond_init( cond );
+ if ( rc ) {
+ ch_free( cond );
+ cond = NULL;
+ }
+ return cond;
+}
+
+static void
+lload_libevent_cond_destroy( void *c )
+{
+ int rc;
+ ldap_pvt_thread_cond_t *cond = c;
+
+ rc = ldap_pvt_thread_cond_destroy( cond );
+ assert( rc == 0 );
+ ch_free( c );
+}
+
+static int
+lload_libevent_cond_signal( void *c, int broadcast )
+{
+ ldap_pvt_thread_cond_t *cond = c;
+
+ if ( broadcast ) {
+ return ldap_pvt_thread_cond_broadcast( cond );
+ } else {
+ return ldap_pvt_thread_cond_signal( cond );
+ }
+}
+
+static int
+lload_libevent_cond_timedwait(
+ void *c,
+ void *lock,
+ const struct timeval *timeout )
+{
+ ldap_pvt_thread_cond_t *cond = c;
+ ldap_pvt_thread_mutex_t *mutex = lock;
+
+ /*
+ * libevent does not seem to request a timeout, this is true as of 2.1.8
+ * that has just been marked the first stable release of the 2.1 series
+ */
+ assert( timeout == NULL );
+
+ return ldap_pvt_thread_cond_wait( cond, mutex );
+}
+
+int
+lload_libevent_init( void )
+{
+ struct evthread_lock_callbacks cbs = {
+ EVTHREAD_LOCK_API_VERSION,
+ EVTHREAD_LOCKTYPE_RECURSIVE,
+ lload_libevent_mutex_init,
+ lload_libevent_mutex_destroy,
+ lload_libevent_mutex_lock,
+ lload_libevent_mutex_unlock
+ };
+ struct evthread_condition_callbacks cond_cbs = {
+ EVTHREAD_CONDITION_API_VERSION,
+ lload_libevent_cond_init,
+ lload_libevent_cond_destroy,
+ lload_libevent_cond_signal,
+ lload_libevent_cond_timedwait
+ };
+
+ if ( ldap_pvt_thread_initialize() ) {
+ return -1;
+ }
+
+ evthread_set_lock_callbacks( &cbs );
+ evthread_set_condition_callbacks( &cond_cbs );
+ evthread_set_id_callback( ldap_pvt_thread_self );
+ return 0;
+}
--- /dev/null
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2015 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/ctype.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+#include <ac/wait.h>
+#include <ac/errno.h>
+
+#include <event2/event.h>
+
+#include "slap.h"
+#include "lutil.h"
+#include "ldif.h"
+
+#ifdef LDAP_SIGCHLD
+static void wait4child( evutil_socket_t sig, short what, void *arg );
+#endif
+
+#ifdef SIGPIPE
+static void sigpipe( evutil_socket_t sig, short what, void *arg );
+#endif
+
+#ifdef HAVE_NT_SERVICE_MANAGER
+#define MAIN_RETURN(x) return
+static struct sockaddr_in bind_addr;
+
+#define SERVICE_EXIT( e, n ) \
+ do { \
+ if ( is_NT_Service ) { \
+ lutil_ServiceStatus.dwWin32ExitCode = (e); \
+ lutil_ServiceStatus.dwServiceSpecificExitCode = (n); \
+ } \
+ } while (0)
+
+#else
+#define SERVICE_EXIT( e, n )
+#define MAIN_RETURN(x) return (x)
+#endif
+
+struct signal_handler {
+ int signal;
+ event_callback_fn handler;
+ struct event *event;
+} signal_handlers[] = {
+ { LDAP_SIGUSR2, slap_sig_shutdown },
+
+#ifdef SIGPIPE
+ { SIGPIPE, sigpipe },
+#endif
+#ifdef SIGHUP
+ { SIGHUP, slap_sig_shutdown },
+#endif
+ { SIGINT, slap_sig_shutdown },
+ { SIGTERM, slap_sig_shutdown },
+#ifdef SIGTRAP
+ { SIGTRAP, slap_sig_shutdown },
+#endif
+#ifdef LDAP_SIGCHLD
+ { LDAP_SIGCHLD, wait4child },
+#endif
+#ifdef SIGBREAK
+ /* SIGBREAK is generated when Ctrl-Break is pressed. */
+ { SIGBREAK, slap_sig_shutdown },
+#endif
+ { 0, NULL }
+};
+
+/*
+ * when more than one lloadd is running on one machine, each one might have
+ * it's own LOCAL for syslogging and must have its own pid/args files
+ */
+
+#ifndef HAVE_MKVERSION
+const char Versionstr[] = OPENLDAP_PACKAGE
+ " " OPENLDAP_VERSION " LDAP Load Balancer Server (lloadd)";
+#endif
+
+#define CHECK_NONE 0x00
+#define CHECK_CONFIG 0x01
+#define CHECK_LOGLEVEL 0x02
+static int check = CHECK_NONE;
+static int version = 0;
+
+void *slap_tls_ctx;
+LDAP *slap_tls_ld;
+
+static int
+slapd_opt_slp( const char *val, void *arg )
+{
+#ifdef HAVE_SLP
+ /* NULL is default */
+ if ( val == NULL || *val == '(' || strcasecmp( val, "on" ) == 0 ) {
+ slapd_register_slp = 1;
+ slapd_slp_attrs = ( val != NULL && *val == '(' ) ? val : NULL;
+
+ } else if ( strcasecmp( val, "off" ) == 0 ) {
+ slapd_register_slp = 0;
+
+ /* NOTE: add support for URL specification? */
+
+ } else {
+ fprintf( stderr, "unrecognized value \"%s\" for SLP option\n", val );
+ return -1;
+ }
+
+ return 0;
+
+#else
+ fputs( "lloadd: SLP support is not available\n", stderr );
+ return 0;
+#endif
+}
+
+/*
+ * Option helper structure:
+ *
+ * oh_nam is left-hand part of <option>[=<value>]
+ * oh_fnc is handler function
+ * oh_arg is an optional arg to oh_fnc
+ * oh_usage is the one-line usage string related to the option,
+ * which is assumed to start with <option>[=<value>]
+ *
+ * please leave valid options in the structure, and optionally #ifdef
+ * their processing inside the helper, so that reasonable and helpful
+ * error messages can be generated if a disabled option is requested.
+ */
+struct option_helper {
+ struct berval oh_name;
+ int (*oh_fnc)( const char *val, void *arg );
+ void *oh_arg;
+ const char *oh_usage;
+} option_helpers[] = {
+ { BER_BVC("slp"), slapd_opt_slp, NULL,
+ "slp[={on|off|(attrs)}] enable/disable SLP using (attrs)" },
+ { BER_BVNULL, 0, NULL, NULL }
+};
+
+#if defined(LDAP_DEBUG) && defined(LDAP_SYSLOG)
+#ifdef LOG_LOCAL4
+int
+parse_syslog_user( const char *arg, int *syslogUser )
+{
+ static slap_verbmasks syslogUsers[] = {
+ { BER_BVC("LOCAL0"), LOG_LOCAL0 },
+ { BER_BVC("LOCAL1"), LOG_LOCAL1 },
+ { BER_BVC("LOCAL2"), LOG_LOCAL2 },
+ { BER_BVC("LOCAL3"), LOG_LOCAL3 },
+ { BER_BVC("LOCAL4"), LOG_LOCAL4 },
+ { BER_BVC("LOCAL5"), LOG_LOCAL5 },
+ { BER_BVC("LOCAL6"), LOG_LOCAL6 },
+ { BER_BVC("LOCAL7"), LOG_LOCAL7 },
+#ifdef LOG_USER
+ { BER_BVC("USER"), LOG_USER },
+#endif /* LOG_USER */
+#ifdef LOG_DAEMON
+ { BER_BVC("DAEMON"), LOG_DAEMON },
+#endif /* LOG_DAEMON */
+ { BER_BVNULL, 0 }
+};
+ int i = verb_to_mask( arg, syslogUsers );
+
+ if ( BER_BVISNULL( &syslogUsers[i].word ) ) {
+ Debug( LDAP_DEBUG_ANY, "unrecognized syslog user \"%s\".\n", arg );
+ return 1;
+ }
+
+ *syslogUser = syslogUsers[i].mask;
+
+ return 0;
+}
+#endif /* LOG_LOCAL4 */
+
+int
+parse_syslog_level( const char *arg, int *levelp )
+{
+ static slap_verbmasks str2syslog_level[] = {
+ { BER_BVC("EMERG"), LOG_EMERG },
+ { BER_BVC("ALERT"), LOG_ALERT },
+ { BER_BVC("CRIT"), LOG_CRIT },
+ { BER_BVC("ERR"), LOG_ERR },
+ { BER_BVC("WARNING"), LOG_WARNING },
+ { BER_BVC("NOTICE"), LOG_NOTICE },
+ { BER_BVC("INFO"), LOG_INFO },
+ { BER_BVC("DEBUG"), LOG_DEBUG },
+ { BER_BVNULL, 0 }
+};
+ int i = verb_to_mask( arg, str2syslog_level );
+ if ( BER_BVISNULL( &str2syslog_level[i].word ) ) {
+ Debug( LDAP_DEBUG_ANY, "unknown syslog level \"%s\".\n", arg );
+ return 1;
+ }
+
+ *levelp = str2syslog_level[i].mask;
+
+ return 0;
+}
+#endif /* LDAP_DEBUG && LDAP_SYSLOG */
+
+int
+parse_debug_unknowns( char **unknowns, int *levelp )
+{
+ int i, level, rc = 0;
+
+ for ( i = 0; unknowns[i] != NULL; i++ ) {
+ level = 0;
+ if ( str2loglevel( unknowns[i], &level ) ) {
+ fprintf( stderr, "unrecognized log level \"%s\"\n", unknowns[i] );
+ rc = 1;
+ } else {
+ *levelp |= level;
+ }
+ }
+ return rc;
+}
+
+int
+parse_debug_level( const char *arg, int *levelp, char ***unknowns )
+{
+ int level;
+
+ if ( arg && arg[0] != '-' && !isdigit( (unsigned char)arg[0] ) ) {
+ int i;
+ char **levels;
+
+ levels = ldap_str2charray( arg, "," );
+
+ for ( i = 0; levels[i] != NULL; i++ ) {
+ level = 0;
+
+ if ( str2loglevel( levels[i], &level ) ) {
+ /* remember this for later */
+ ldap_charray_add( unknowns, levels[i] );
+ fprintf( stderr, "unrecognized log level \"%s\" (deferred)\n",
+ levels[i] );
+ } else {
+ *levelp |= level;
+ }
+ }
+
+ ldap_charray_free( levels );
+
+ } else {
+ int rc;
+
+ if ( arg[0] == '-' ) {
+ rc = lutil_atoix( &level, arg, 0 );
+ } else {
+ unsigned ulevel;
+
+ rc = lutil_atoux( &ulevel, arg, 0 );
+ level = (int)ulevel;
+ }
+
+ if ( rc ) {
+ fprintf( stderr,
+ "unrecognized log level "
+ "\"%s\"\n",
+ arg );
+ return 1;
+ }
+
+ if ( level == 0 ) {
+ *levelp = 0;
+
+ } else {
+ *levelp |= level;
+ }
+ }
+
+ return 0;
+}
+
+static void
+usage( char *name )
+{
+ fprintf( stderr, "usage: %s options\n", name );
+ fprintf( stderr,
+ "\t-4\t\tIPv4 only\n"
+ "\t-6\t\tIPv6 only\n"
+ "\t-d level\tDebug level"
+ "\n"
+ "\t-f filename\tConfiguration file\n"
+#if defined(HAVE_SETUID) && defined(HAVE_SETGID)
+ "\t-g group\tGroup (id or name) to run as\n"
+#endif
+ "\t-h URLs\t\tList of URLs to serve\n"
+#ifdef SLAP_DEFAULT_SYSLOG_USER
+ "\t-l facility\tSyslog facility (default: LOCAL4)\n"
+#endif
+ "\t-n serverName\tService name\n"
+ "\t-o <opt>[=val] generic means to specify options" );
+ if ( !BER_BVISNULL( &option_helpers[0].oh_name ) ) {
+ int i;
+
+ fprintf( stderr, "; supported options:\n" );
+ for ( i = 0; !BER_BVISNULL( &option_helpers[i].oh_name ); i++ ) {
+ fprintf( stderr, "\t\t%s\n", option_helpers[i].oh_usage );
+ }
+ } else {
+ fprintf( stderr, "\n" );
+ }
+ fprintf( stderr,
+#ifdef HAVE_CHROOT
+ "\t-r directory\tSandbox directory to chroot to\n"
+#endif
+ "\t-s level\tSyslog level\n"
+ "\t-t\t\tCheck configuration file\n"
+#if defined(HAVE_SETUID) && defined(HAVE_SETGID)
+ "\t-u user\t\tUser (id or name) to run as\n"
+#endif
+ "\t-V\t\tprint version info (-VV exit afterwards)\n" );
+}
+
+#ifdef HAVE_NT_SERVICE_MANAGER
+void WINAPI
+ServiceMain( DWORD argc, LPTSTR *argv )
+#else
+int
+main( int argc, char **argv )
+#endif
+{
+ int i, no_detach = 0;
+ int rc = 1;
+ char *urls = NULL;
+#if defined(HAVE_SETUID) && defined(HAVE_SETGID)
+ char *username = NULL;
+ char *groupname = NULL;
+#endif
+#if defined(HAVE_CHROOT)
+ char *sandbox = NULL;
+#endif
+#ifdef SLAP_DEFAULT_SYSLOG_USER
+ int syslogUser = SLAP_DEFAULT_SYSLOG_USER;
+#endif
+
+#ifndef HAVE_WINSOCK
+ int pid, waitfds[2];
+#endif
+ int g_argc = argc;
+ char **g_argv = argv;
+
+ char *configfile = NULL;
+ char *configdir = NULL;
+ char *serverName;
+ int serverMode = SLAP_SERVER_MODE;
+
+ char **debug_unknowns = NULL;
+ char **syslog_unknowns = NULL;
+
+ int slapd_pid_file_unlink = 0, slapd_args_file_unlink = 0;
+ int firstopt = 1;
+
+ struct event_base *daemon_base = NULL;
+
+ slap_sl_mem_init();
+
+ (void)lload_libevent_init();
+
+ serverName = lutil_progname( "lloadd", argc, argv );
+
+#ifdef HAVE_NT_SERVICE_MANAGER
+ {
+ int *ip;
+ char *newConfigFile;
+ char *newConfigDir;
+ char *newUrls;
+ char *regService = NULL;
+
+ if ( is_NT_Service ) {
+ lutil_CommenceStartupProcessing( serverName, slap_sig_shutdown );
+ if ( strcmp( serverName, SERVICE_NAME ) ) regService = serverName;
+ }
+
+ ip = (int *)lutil_getRegParam( regService, "DebugLevel" );
+ if ( ip != NULL ) {
+ slap_debug = *ip;
+ Debug( LDAP_DEBUG_ANY, "new debug level from registry is: %d\n",
+ slap_debug );
+ }
+
+ newUrls = (char *)lutil_getRegParam( regService, "Urls" );
+ if ( newUrls ) {
+ if ( urls ) ch_free( urls );
+
+ urls = ch_strdup( newUrls );
+ Debug( LDAP_DEBUG_ANY, "new urls from registry: %s\n", urls );
+ }
+
+ newConfigFile = (char *)lutil_getRegParam( regService, "ConfigFile" );
+ if ( newConfigFile != NULL ) {
+ configfile = ch_strdup( newConfigFile );
+ Debug( LDAP_DEBUG_ANY, "new config file from registry is: %s\n",
+ configfile );
+ }
+
+ newConfigDir = (char *)lutil_getRegParam( regService, "ConfigDir" );
+ if ( newConfigDir != NULL ) {
+ configdir = ch_strdup( newConfigDir );
+ Debug( LDAP_DEBUG_ANY, "new config dir from registry is: %s\n",
+ configdir );
+ }
+ }
+#endif
+
+ while ( (i = getopt( argc, argv,
+ "c:d:f:F:h:n:o:s:tV"
+#ifdef LDAP_PF_INET6
+ "46"
+#endif
+#ifdef HAVE_CHROOT
+ "r:"
+#endif
+#if defined(LDAP_DEBUG) && defined(LDAP_SYSLOG)
+ "S:"
+#ifdef LOG_LOCAL4
+ "l:"
+#endif
+#endif
+#if defined(HAVE_SETUID) && defined(HAVE_SETGID)
+ "u:g:"
+#endif
+ )) != EOF ) {
+ switch ( i ) {
+#ifdef LDAP_PF_INET6
+ case '4':
+ slap_inet4or6 = AF_INET;
+ break;
+ case '6':
+ slap_inet4or6 = AF_INET6;
+ break;
+#endif
+
+ case 'h': /* listen URLs */
+ if ( urls != NULL ) free( urls );
+ urls = ch_strdup( optarg );
+ break;
+
+ case 'd': { /* set debug level and 'do not detach' flag */
+ int level = 0;
+
+ if ( strcmp( optarg, "?" ) == 0 ) {
+ check |= CHECK_LOGLEVEL;
+ break;
+ }
+
+ no_detach = 1;
+ if ( parse_debug_level( optarg, &level, &debug_unknowns ) ) {
+ goto destroy;
+ }
+#ifdef LDAP_DEBUG
+ slap_debug |= level;
+#else
+ if ( level != 0 )
+ fputs( "must compile with LDAP_DEBUG for debugging\n",
+ stderr );
+#endif
+ } break;
+
+ case 'f': /* read config file */
+ configfile = ch_strdup( optarg );
+ break;
+
+ case 'o': {
+ char *val = strchr( optarg, '=' );
+ struct berval opt;
+
+ opt.bv_val = optarg;
+
+ if ( val ) {
+ opt.bv_len = ( val - optarg );
+ val++;
+
+ } else {
+ opt.bv_len = strlen( optarg );
+ }
+
+ for ( i = 0; !BER_BVISNULL( &option_helpers[i].oh_name );
+ i++ ) {
+ if ( ber_bvstrcasecmp( &option_helpers[i].oh_name, &opt ) ==
+ 0 ) {
+ assert( option_helpers[i].oh_fnc != NULL );
+ if ( (*option_helpers[i].oh_fnc)(
+ val, option_helpers[i].oh_arg ) == -1 ) {
+ /* we assume the option parsing helper
+ * issues appropriate and self-explanatory
+ * error messages... */
+ goto stop;
+ }
+ break;
+ }
+ }
+
+ if ( BER_BVISNULL( &option_helpers[i].oh_name ) ) {
+ goto unhandled_option;
+ }
+ break;
+ }
+
+ case 's': /* set syslog level */
+ if ( strcmp( optarg, "?" ) == 0 ) {
+ check |= CHECK_LOGLEVEL;
+ break;
+ }
+
+ if ( parse_debug_level(
+ optarg, &ldap_syslog, &syslog_unknowns ) ) {
+ goto destroy;
+ }
+ break;
+
+#if defined(LDAP_DEBUG) && defined(LDAP_SYSLOG)
+ case 'S':
+ if ( parse_syslog_level( optarg, &ldap_syslog_level ) ) {
+ goto destroy;
+ }
+ break;
+
+#ifdef LOG_LOCAL4
+ case 'l': /* set syslog local user */
+ if ( parse_syslog_user( optarg, &syslogUser ) ) {
+ goto destroy;
+ }
+ break;
+#endif
+#endif /* LDAP_DEBUG && LDAP_SYSLOG */
+
+#ifdef HAVE_CHROOT
+ case 'r':
+ if ( sandbox ) free( sandbox );
+ sandbox = ch_strdup( optarg );
+ break;
+#endif
+
+#if defined(HAVE_SETUID) && defined(HAVE_SETGID)
+ case 'u': /* user name */
+ if ( username ) free( username );
+ username = ch_strdup( optarg );
+ break;
+
+ case 'g': /* group name */
+ if ( groupname ) free( groupname );
+ groupname = ch_strdup( optarg );
+ break;
+#endif /* SETUID && GETUID */
+
+ case 'n': /* NT service name */
+ serverName = ch_strdup( optarg );
+ break;
+
+ case 't':
+ check |= CHECK_CONFIG;
+ break;
+
+ case 'V':
+ version++;
+ break;
+
+ default:
+unhandled_option:;
+ usage( argv[0] );
+ rc = 1;
+ SERVICE_EXIT( ERROR_SERVICE_SPECIFIC_ERROR, 15 );
+ goto stop;
+ }
+
+ if ( firstopt ) {
+ firstopt = 0;
+ }
+ }
+
+ if ( optind != argc ) goto unhandled_option;
+
+ ber_set_option( NULL, LBER_OPT_DEBUG_LEVEL, &slap_debug );
+ ldap_set_option( NULL, LDAP_OPT_DEBUG_LEVEL, &slap_debug );
+ ldif_debug = slap_debug;
+
+ if ( version ) {
+ fprintf( stderr, "%s\n", Versionstr );
+
+ if ( version > 1 ) goto stop;
+ }
+
+#if defined(LDAP_DEBUG) && defined(LDAP_SYSLOG)
+ {
+ char *logName;
+#ifdef HAVE_EBCDIC
+ logName = ch_strdup( serverName );
+ __atoe( logName );
+#else
+ logName = serverName;
+#endif
+
+#ifdef LOG_LOCAL4
+ openlog( logName, OPENLOG_OPTIONS, syslogUser );
+#elif defined LOG_DEBUG
+ openlog( logName, OPENLOG_OPTIONS );
+#endif
+#ifdef HAVE_EBCDIC
+ free( logName );
+#endif
+ }
+#endif /* LDAP_DEBUG && LDAP_SYSLOG */
+
+ Debug( LDAP_DEBUG_ANY, "%s", Versionstr );
+
+ global_host = ldap_pvt_get_fqdn( NULL );
+
+ if ( check == CHECK_NONE && slapd_daemon_init( urls ) != 0 ) {
+ rc = 1;
+ SERVICE_EXIT( ERROR_SERVICE_SPECIFIC_ERROR, 16 );
+ goto stop;
+ }
+
+#if defined(HAVE_CHROOT)
+ if ( sandbox ) {
+ if ( chdir( sandbox ) ) {
+ perror( "chdir" );
+ rc = 1;
+ goto stop;
+ }
+ if ( chroot( sandbox ) ) {
+ perror( "chroot" );
+ rc = 1;
+ goto stop;
+ }
+ if ( chdir( "/" ) ) {
+ perror( "chdir" );
+ rc = 1;
+ goto stop;
+ }
+ }
+#endif
+
+#if defined(HAVE_SETUID) && defined(HAVE_SETGID)
+ if ( username != NULL || groupname != NULL ) {
+ slap_init_user( username, groupname );
+ }
+#endif
+
+#ifdef HAVE_TLS
+ rc = ldap_create( &slap_tls_ld );
+ if ( rc ) {
+ SERVICE_EXIT( ERROR_SERVICE_SPECIFIC_ERROR, 20 );
+ goto destroy;
+ }
+ /* Library defaults to full certificate checking. This is correct when
+ * a client is verifying a server because all servers should have a
+ * valid cert. But few clients have valid certs, so we want our default
+ * to be no checking. The config file can override this as usual.
+ */
+ rc = LDAP_OPT_X_TLS_NEVER;
+ (void)ldap_pvt_tls_set_option(
+ slap_tls_ld, LDAP_OPT_X_TLS_REQUIRE_CERT, &rc );
+#endif
+
+ rc = slap_init( serverMode, serverName );
+ if ( rc ) {
+ SERVICE_EXIT( ERROR_SERVICE_SPECIFIC_ERROR, 18 );
+ goto destroy;
+ }
+
+ if ( read_config( configfile, configdir ) != 0 ) {
+ rc = 1;
+ SERVICE_EXIT( ERROR_SERVICE_SPECIFIC_ERROR, 19 );
+
+ if ( check & CHECK_CONFIG ) {
+ fprintf( stderr, "config check failed\n" );
+ }
+
+ goto destroy;
+ }
+
+ if ( debug_unknowns ) {
+ rc = parse_debug_unknowns( debug_unknowns, &slap_debug );
+ ldap_charray_free( debug_unknowns );
+ debug_unknowns = NULL;
+ if ( rc ) goto destroy;
+ }
+ if ( syslog_unknowns ) {
+ rc = parse_debug_unknowns( syslog_unknowns, &ldap_syslog );
+ ldap_charray_free( syslog_unknowns );
+ syslog_unknowns = NULL;
+ if ( rc ) goto destroy;
+ }
+
+ if ( check & CHECK_LOGLEVEL ) {
+ rc = 0;
+ goto destroy;
+ }
+
+ if ( check & CHECK_CONFIG ) {
+ fprintf( stderr, "config check succeeded\n" );
+
+ check &= ~CHECK_CONFIG;
+ if ( check == CHECK_NONE ) {
+ rc = 0;
+ goto destroy;
+ }
+ }
+
+#ifdef HAVE_TLS
+ rc = ldap_pvt_tls_init();
+ if ( rc != 0 ) {
+ Debug( LDAP_DEBUG_ANY, "main: "
+ "TLS init failed: %d\n",
+ rc );
+ rc = 1;
+ SERVICE_EXIT( ERROR_SERVICE_SPECIFIC_ERROR, 20 );
+ goto destroy;
+ }
+
+ {
+ int opt = 1;
+
+ /* Force new ctx to be created */
+ rc = ldap_pvt_tls_set_option(
+ slap_tls_ld, LDAP_OPT_X_TLS_NEWCTX, &opt );
+ if ( rc == 0 ) {
+ /* The ctx's refcount is bumped up here */
+ ldap_pvt_tls_get_option(
+ slap_tls_ld, LDAP_OPT_X_TLS_CTX, &slap_tls_ctx );
+ } else if ( rc != LDAP_NOT_SUPPORTED ) {
+ Debug( LDAP_DEBUG_ANY, "main: "
+ "TLS init def ctx failed: %d\n",
+ rc );
+ rc = 1;
+ SERVICE_EXIT( ERROR_SERVICE_SPECIFIC_ERROR, 20 );
+ goto destroy;
+ }
+ }
+#endif
+
+ daemon_base = event_base_new();
+ if ( !daemon_base ) {
+ Debug( LDAP_DEBUG_ANY, "main: "
+ "main event base allocation failed\n" );
+ rc = 1;
+ SERVICE_EXIT( ERROR_SERVICE_SPECIFIC_ERROR, 21 );
+ goto destroy;
+ }
+
+ for ( i = 0; signal_handlers[i].signal; i++ ) {
+ struct event *event;
+ event = evsignal_new( daemon_base, signal_handlers[i].signal,
+ signal_handlers[i].handler, daemon_base );
+ if ( !event || event_add( event, NULL ) ) {
+ Debug( LDAP_DEBUG_ANY, "main: "
+ "failed to register a handler for signal %d\n",
+ signal_handlers[i].signal );
+ rc = 1;
+ SERVICE_EXIT( ERROR_SERVICE_SPECIFIC_ERROR, 21 );
+ goto destroy;
+ }
+ signal_handlers[i].event = event;
+ }
+
+#ifndef HAVE_WINSOCK
+ if ( !no_detach ) {
+ if ( lutil_pair( waitfds ) < 0 ) {
+ Debug( LDAP_DEBUG_ANY, "main: "
+ "lutil_pair failed\n" );
+ rc = 1;
+ goto destroy;
+ }
+ pid = lutil_detach( no_detach, 0 );
+ if ( pid ) {
+ char buf[4];
+ rc = EXIT_SUCCESS;
+ close( waitfds[1] );
+ if ( read( waitfds[0], buf, 1 ) != 1 ) rc = EXIT_FAILURE;
+ _exit( rc );
+ } else {
+ close( waitfds[0] );
+ }
+ }
+#endif /* HAVE_WINSOCK */
+
+ if ( slapd_pid_file != NULL ) {
+ FILE *fp = fopen( slapd_pid_file, "w" );
+
+ if ( fp == NULL ) {
+ char ebuf[128];
+ int save_errno = errno;
+
+ Debug( LDAP_DEBUG_ANY, "unable to open pid file "
+ "\"%s\": %d (%s)\n",
+ slapd_pid_file, save_errno,
+ AC_STRERROR_R( save_errno, ebuf, sizeof(ebuf) ) );
+
+ free( slapd_pid_file );
+ slapd_pid_file = NULL;
+
+ rc = 1;
+ goto destroy;
+ }
+ fprintf( fp, "%d\n", (int)getpid() );
+ fclose( fp );
+ slapd_pid_file_unlink = 1;
+ }
+
+ if ( slapd_args_file != NULL ) {
+ FILE *fp = fopen( slapd_args_file, "w" );
+
+ if ( fp == NULL ) {
+ char ebuf[128];
+ int save_errno = errno;
+
+ Debug( LDAP_DEBUG_ANY, "unable to open args file "
+ "\"%s\": %d (%s)\n",
+ slapd_args_file, save_errno,
+ AC_STRERROR_R( save_errno, ebuf, sizeof(ebuf) ) );
+
+ free( slapd_args_file );
+ slapd_args_file = NULL;
+
+ rc = 1;
+ goto destroy;
+ }
+
+ for ( i = 0; i < g_argc; i++ ) {
+ fprintf( fp, "%s ", g_argv[i] );
+ }
+ fprintf( fp, "\n" );
+ fclose( fp );
+ slapd_args_file_unlink = 1;
+ }
+
+ /*
+ * FIXME: moved here from slapd_daemon_task()
+ * because back-monitor db_open() needs it
+ */
+ time( &starttime );
+
+ Debug( LDAP_DEBUG_ANY, "lloadd starting\n" );
+
+#ifndef HAVE_WINSOCK
+ if ( !no_detach ) {
+ write( waitfds[1], "1", 1 );
+ close( waitfds[1] );
+ }
+#endif
+
+#ifdef HAVE_NT_EVENT_LOG
+ if ( is_NT_Service )
+ lutil_LogStartedEvent( serverName, slap_debug,
+ configfile ? configfile : LLOADD_DEFAULT_CONFIGFILE, urls );
+#endif
+
+ rc = slapd_daemon( daemon_base );
+
+#ifdef HAVE_NT_SERVICE_MANAGER
+ /* Throw away the event that we used during the startup process. */
+ if ( is_NT_Service ) ldap_pvt_thread_cond_destroy( &started_event );
+#endif
+
+destroy:
+ if ( check & CHECK_LOGLEVEL ) {
+ (void)loglevel_print( stdout );
+ }
+ /* remember an error during destroy */
+ rc |= slap_destroy();
+
+stop:
+#ifdef HAVE_NT_EVENT_LOG
+ if ( is_NT_Service ) lutil_LogStoppedEvent( serverName );
+#endif
+
+ Debug( LDAP_DEBUG_ANY, "lloadd stopped.\n" );
+
+#ifdef HAVE_NT_SERVICE_MANAGER
+ lutil_ReportShutdownComplete();
+#endif
+
+#ifdef LOG_DEBUG
+ closelog();
+#endif
+ slapd_daemon_destroy();
+
+#ifdef HAVE_TLS
+ if ( slap_tls_ld ) {
+ ldap_pvt_tls_ctx_free( slap_tls_ctx );
+ ldap_unbind_ext( slap_tls_ld, NULL, NULL );
+ }
+ ldap_pvt_tls_destroy();
+#endif
+
+ if ( slapd_pid_file_unlink ) {
+ unlink( slapd_pid_file );
+ }
+ if ( slapd_args_file_unlink ) {
+ unlink( slapd_args_file );
+ }
+
+ config_destroy();
+
+ if ( configfile ) ch_free( configfile );
+ if ( configdir ) ch_free( configdir );
+ if ( urls ) ch_free( urls );
+ if ( global_host ) ch_free( global_host );
+
+ /* kludge, get symbols referenced */
+ tavl_free( NULL, NULL );
+
+ MAIN_RETURN(rc);
+}
+
+#ifdef SIGPIPE
+
+/*
+ * Catch and discard terminated child processes, to avoid zombies.
+ */
+
+static void
+sigpipe( evutil_socket_t sig, short what, void *arg )
+{
+}
+
+#endif /* SIGPIPE */
+
+#ifdef LDAP_SIGCHLD
+
+/*
+ * Catch and discard terminated child processes, to avoid zombies.
+ */
+
+static void
+wait4child( evutil_socket_t sig, short what, void *arg )
+{
+ int save_errno = errno;
+
+#ifdef WNOHANG
+ do
+ errno = 0;
+#ifdef HAVE_WAITPID
+ while ( waitpid( (pid_t)-1, NULL, WNOHANG ) > 0 || errno == EINTR );
+#else
+ while ( wait3( NULL, WNOHANG, NULL ) > 0 || errno == EINTR );
+#endif
+#else
+ (void)wait( NULL );
+#endif
+ errno = save_errno;
+}
+
+#endif /* LDAP_SIGCHLD */
--- /dev/null
+../slapd/nt_svc.c
\ No newline at end of file
--- /dev/null
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2015 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#ifndef PROTO_SLAP_H
+#define PROTO_SLAP_H
+
+#include <ldap_cdefs.h>
+#include "ldap_pvt.h"
+
+#include <event2/event.h>
+
+LDAP_BEGIN_DECL
+
+struct config_args_s; /* config.h */
+struct config_reply_s; /* config.h */
+
+/*
+ * ch_malloc.c
+ */
+LDAP_SLAPD_V (BerMemoryFunctions) ch_mfuncs;
+LDAP_SLAPD_F (void *) ch_malloc( ber_len_t size );
+LDAP_SLAPD_F (void *) ch_realloc( void *block, ber_len_t size );
+LDAP_SLAPD_F (void *) ch_calloc( ber_len_t nelem, ber_len_t size );
+LDAP_SLAPD_F (char *) ch_strdup( const char *string );
+LDAP_SLAPD_F (void) ch_free( void * );
+
+#ifndef CH_FREE
+#undef free
+#define free ch_free
+#endif
+
+/*
+ * config.c
+ */
+LDAP_SLAPD_F (int) read_config( const char *fname, const char *dir );
+LDAP_SLAPD_F (void) config_destroy( void );
+LDAP_SLAPD_F (int) verb_to_mask( const char *word, slap_verbmasks *v );
+LDAP_SLAPD_F (int) str2loglevel( const char *s, int *l );
+LDAP_SLAPD_F (void) bindconf_free( slap_bindconf *bc );
+
+/*
+ * connection.c
+ */
+LDAP_SLAPD_F (Connection *) connection_init( ber_socket_t s, const char *peername, int use_tls );
+LDAP_SLAPD_F (Connection *) client_init( ber_socket_t s, Listener *url, const char *peername, struct event_base *base, int use_tls );
+LDAP_SLAPD_F (void) connection_destroy( Connection *c );
+
+/*
+ * daemon.c
+ */
+LDAP_SLAPD_F (int) slapd_daemon_init( const char *urls );
+LDAP_SLAPD_F (int) slapd_daemon_destroy( void );
+LDAP_SLAPD_F (int) slapd_daemon( struct event_base *daemon_base );
+LDAP_SLAPD_F (Listener **) slapd_get_listeners( void );
+
+LDAP_SLAPD_F (void) slap_sig_shutdown( evutil_socket_t sig, short what, void *arg );
+
+LDAP_SLAPD_V (volatile sig_atomic_t) slapd_shutdown;
+LDAP_SLAPD_V (int) lloadd_inited;
+LDAP_SLAPD_V (struct runqueue_s) slapd_rq;
+LDAP_SLAPD_V (int) slapd_daemon_threads;
+LDAP_SLAPD_V (int) slapd_daemon_mask;
+#ifdef LDAP_TCP_BUFFER
+LDAP_SLAPD_V (int) slapd_tcp_rmem;
+LDAP_SLAPD_V (int) slapd_tcp_wmem;
+#endif /* LDAP_TCP_BUFFER */
+
+#define bvmatch( bv1, bv2 ) \
+ ( ( (bv1)->bv_len == (bv2)->bv_len ) && \
+ ( memcmp( (bv1)->bv_val, (bv2)->bv_val, (bv1)->bv_len ) == 0 ) )
+
+/*
+ * globals.c
+ */
+LDAP_SLAPD_V (const struct berval) slap_empty_bv;
+LDAP_SLAPD_V (const struct berval) slap_unknown_bv;
+LDAP_SLAPD_V (const struct berval) slap_true_bv;
+LDAP_SLAPD_V (const struct berval) slap_false_bv;
+LDAP_SLAPD_V (struct slap_sync_cookie_s) slap_sync_cookie;
+LDAP_SLAPD_V (void *) slap_tls_ctx;
+LDAP_SLAPD_V (LDAP *) slap_tls_ld;
+
+/*
+ * init.c
+ */
+LDAP_SLAPD_F (int) slap_init( int mode, const char *name );
+LDAP_SLAPD_F (int) slap_destroy( void );
+
+/*
+ * libevent_support.c
+ */
+LDAP_SLAPD_F (int) lload_libevent_init( void );
+
+/*
+ * main.c
+ */
+LDAP_SLAPD_V (int) slapd_register_slp;
+LDAP_SLAPD_V (const char *) slapd_slp_attrs;
+
+/*
+ * sl_malloc.c
+ */
+LDAP_SLAPD_F (void *) slap_sl_malloc( ber_len_t size, void *ctx );
+LDAP_SLAPD_F (void *) slap_sl_realloc( void *block, ber_len_t size, void *ctx );
+LDAP_SLAPD_F (void *) slap_sl_calloc( ber_len_t nelem, ber_len_t size, void *ctx );
+LDAP_SLAPD_F (void) slap_sl_free( void *, void *ctx );
+
+LDAP_SLAPD_V (BerMemoryFunctions) slap_sl_mfuncs;
+
+LDAP_SLAPD_F (void) slap_sl_mem_init( void );
+LDAP_SLAPD_F (void *) slap_sl_mem_create( ber_len_t size, int stack, void *ctx, int flag );
+LDAP_SLAPD_F (void) slap_sl_mem_setctx( void *ctx, void *memctx );
+LDAP_SLAPD_F (void) slap_sl_mem_destroy( void *key, void *data );
+LDAP_SLAPD_F (void *) slap_sl_context( void *ptr );
+
+/* assumes (x) > (y) returns 1 if true, 0 otherwise */
+#define SLAP_PTRCMP(x, y) ( (x) < (y) ? -1 : (x) > (y) )
+
+/*
+ * user.c
+ */
+#if defined(HAVE_PWD_H) && defined(HAVE_GRP_H)
+LDAP_SLAPD_F (void) slap_init_user( char *username, char *groupname );
+#endif
+
+/*
+ * value.c
+ */
+LDAP_SLAPD_F (int) value_add_one( BerVarray *vals, struct berval *addval );
+
+#ifdef SLAP_ZONE_ALLOC
+/*
+ * zn_malloc.c
+ */
+LDAP_SLAPD_F (void *) slap_zn_malloc( ber_len_t, void * );
+LDAP_SLAPD_F (void *) slap_zn_realloc( void *, ber_len_t, void * );
+LDAP_SLAPD_F (void *) slap_zn_calloc( ber_len_t, ber_len_t, void * );
+LDAP_SLAPD_F (void) slap_zn_free( void *, void * );
+
+LDAP_SLAPD_F (void *) slap_zn_mem_create( ber_len_t, ber_len_t, ber_len_t, ber_len_t );
+LDAP_SLAPD_F (void) slap_zn_mem_destroy( void * );
+LDAP_SLAPD_F (int) slap_zn_validate( void *, void *, int );
+LDAP_SLAPD_F (int) slap_zn_invalidate( void *, void * );
+LDAP_SLAPD_F (int) slap_zh_rlock( void * );
+LDAP_SLAPD_F (int) slap_zh_runlock( void * );
+LDAP_SLAPD_F (int) slap_zh_wlock( void * );
+LDAP_SLAPD_F (int) slap_zh_wunlock( void * );
+LDAP_SLAPD_F (int) slap_zn_rlock( void *, void * );
+LDAP_SLAPD_F (int) slap_zn_runlock( void *, void * );
+LDAP_SLAPD_F (int) slap_zn_wlock( void *, void * );
+LDAP_SLAPD_F (int) slap_zn_wunlock( void *, void * );
+#endif
+
+LDAP_SLAPD_V (ber_len_t) sockbuf_max_incoming;
+LDAP_SLAPD_V (ber_len_t) sockbuf_max_incoming_auth;
+
+LDAP_SLAPD_V (slap_mask_t) global_allows;
+LDAP_SLAPD_V (slap_mask_t) global_disallows;
+
+LDAP_SLAPD_V (const char) Versionstr[];
+
+LDAP_SLAPD_V (int) global_gentlehup;
+LDAP_SLAPD_V (int) global_idletimeout;
+LDAP_SLAPD_V (char *) global_host;
+LDAP_SLAPD_V (int) lber_debug;
+LDAP_SLAPD_V (int) ldap_syslog;
+
+LDAP_SLAPD_V (char *) slapd_pid_file;
+LDAP_SLAPD_V (char *) slapd_args_file;
+LDAP_SLAPD_V (time_t) starttime;
+
+/* use time(3) -- no mutex */
+#define slap_get_time() time( NULL )
+
+LDAP_SLAPD_V (ldap_pvt_thread_pool_t) connection_pool;
+LDAP_SLAPD_V (int) connection_pool_max;
+LDAP_SLAPD_V (int) connection_pool_queues;
+LDAP_SLAPD_V (int) slap_tool_thread_max;
+
+#ifdef USE_MP_BIGNUM
+#define UI2BVX( bv, ui, ctx ) \
+ do { \
+ char *val; \
+ ber_len_t len; \
+ val = BN_bn2dec( ui ); \
+ if (val) { \
+ len = strlen( val ); \
+ if ( len > (bv)->bv_len ) { \
+ (bv)->bv_val = ber_memrealloc_x( (bv)->bv_val, len + 1, (ctx) ); \
+ } \
+ AC_MEMCPY( (bv)->bv_val, val, len + 1 ); \
+ (bv)->bv_len = len; \
+ OPENSSL_free( val ); \
+ } else { \
+ ber_memfree_x( (bv)->bv_val, (ctx) ); \
+ BER_BVZERO( (bv) ); \
+ } \
+ } while (0)
+
+#elif defined(USE_MP_GMP)
+/* NOTE: according to the documentation, the result
+ * of mpz_sizeinbase() can exceed the length of the
+ * string representation of the number by 1
+ */
+#define UI2BVX( bv, ui, ctx ) \
+ do { \
+ ber_len_t len = mpz_sizeinbase( (ui), 10 ); \
+ if ( len > (bv)->bv_len ) { \
+ (bv)->bv_val = ber_memrealloc_x( (bv)->bv_val, len + 1, (ctx) ); \
+ } \
+ (void)mpz_get_str( (bv)->bv_val, 10, (ui) ); \
+ if ( (bv)->bv_val[len - 1] == '\0' ) { \
+ len--; \
+ } \
+ (bv)->bv_len = len; \
+ } while (0)
+
+#else
+#ifdef USE_MP_LONG_LONG
+#define UI2BV_FORMAT "%llu"
+#elif defined USE_MP_LONG
+#define UI2BV_FORMAT "%lu"
+#elif defined HAVE_LONG_LONG
+#define UI2BV_FORMAT "%llu"
+#else
+#define UI2BV_FORMAT "%lu"
+#endif
+
+#define UI2BVX( bv, ui, ctx ) \
+ do { \
+ char buf[LDAP_PVT_INTTYPE_CHARS(long)]; \
+ ber_len_t len; \
+ len = snprintf( buf, sizeof( buf ), UI2BV_FORMAT, (ui) ); \
+ if ( len > (bv)->bv_len ) { \
+ (bv)->bv_val = ber_memrealloc_x( (bv)->bv_val, len + 1, (ctx) ); \
+ } \
+ (bv)->bv_len = len; \
+ AC_MEMCPY( (bv)->bv_val, buf, len + 1 ); \
+ } while (0)
+#endif
+
+#define UI2BV( bv, ui ) UI2BVX( bv, ui, NULL )
+
+LDAP_END_DECL
+
+#endif /* PROTO_SLAP_H */
--- /dev/null
+../slapd/sl_malloc.c
\ No newline at end of file
--- /dev/null
+/* slap.h - stand alone ldap server include file */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2015 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#ifndef _SLAP_H_
+#define _SLAP_H_
+
+#include "ldap_defaults.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+
+#include <sys/types.h>
+#include <ac/syslog.h>
+#include <ac/regex.h>
+#include <ac/signal.h>
+#include <ac/socket.h>
+#include <ac/time.h>
+#include <ac/param.h>
+
+#include "avl.h"
+
+#ifndef ldap_debug
+#define ldap_debug slap_debug
+#endif
+
+#include "ldap_log.h"
+
+#include <ldap.h>
+#include <ldap_schema.h>
+
+#include "lber_pvt.h"
+#include "ldap_pvt.h"
+#include "ldap_pvt_thread.h"
+#include "ldap_queue.h"
+
+#include <event2/event.h>
+
+LDAP_BEGIN_DECL
+
+/*
+ * SLAPD Memory allocation macros
+ *
+ * Unlike ch_*() routines, these routines do not assert() upon
+ * allocation error. They are intended to be used instead of
+ * ch_*() routines where the caller has implemented proper
+ * checking for and handling of allocation errors.
+ *
+ * Patches to convert ch_*() calls to SLAP_*() calls welcomed.
+ */
+#define SLAP_MALLOC(s) ber_memalloc( ( s ) )
+#define SLAP_CALLOC(n, s) ber_memcalloc( ( n ), ( s ) )
+#define SLAP_REALLOC(p, s) ber_memrealloc( ( p ), ( s ) )
+#define SLAP_FREE(p) ber_memfree( ( p ) )
+#define SLAP_VFREE(v) ber_memvfree( (void **)( v ) )
+#define SLAP_STRDUP(s) ber_strdup( ( s ) )
+#define SLAP_STRNDUP(s, l) ber_strndup( ( s ), ( l ) )
+
+#define SERVICE_NAME OPENLDAP_PACKAGE "-slapd"
+#define SLAPD_ANONYMOUS ""
+#define SLAP_STRING_UNKNOWN "unknown"
+
+#define SLAP_MAX_WORKER_THREADS ( 16 )
+
+#define SLAP_SB_MAX_INCOMING_DEFAULT ( ( 1 << 18 ) - 1 )
+#define SLAP_SB_MAX_INCOMING_AUTH ( ( 1 << 24 ) - 1 )
+
+#define SLAP_TEXT_BUFLEN ( 256 )
+
+/* unknown config file directive */
+#define SLAP_CONF_UNKNOWN ( -1026 )
+
+LDAP_SLAPD_V (int) slap_debug;
+
+typedef unsigned long slap_mask_t;
+
+typedef struct Connection Connection;
+/* end of forward declarations */
+
+typedef union Sockaddr {
+ struct sockaddr sa_addr;
+ struct sockaddr_in sa_in_addr;
+#ifdef LDAP_PF_INET6
+ struct sockaddr_storage sa_storage;
+ struct sockaddr_in6 sa_in6_addr;
+#endif
+#ifdef LDAP_PF_LOCAL
+ struct sockaddr_un sa_un_addr;
+#endif
+} Sockaddr;
+
+#ifdef LDAP_PF_INET6
+extern int slap_inet4or6;
+#endif
+
+LDAP_SLAPD_V (int) slapMode;
+#define SLAP_UNDEFINED_MODE 0x0000
+#define SLAP_SERVER_MODE 0x0001
+#define SLAP_TOOL_MODE 0x0002
+#define SLAP_MODE 0x0003
+
+#define SLAP_SERVER_RUNNING 0x8000
+
+#define SB_TLS_DEFAULT ( -1 )
+#define SB_TLS_OFF 0
+#define SB_TLS_ON 1
+#define SB_TLS_CRITICAL 2
+
+typedef struct slap_keepalive {
+ int sk_idle;
+ int sk_probes;
+ int sk_interval;
+} slap_keepalive;
+
+typedef struct slap_bindconf {
+ struct berval sb_uri;
+ int sb_version;
+ int sb_tls;
+ int sb_method;
+ int sb_timeout_api;
+ int sb_timeout_net;
+ struct berval sb_binddn;
+ struct berval sb_cred;
+ struct berval sb_saslmech;
+ char *sb_secprops;
+ struct berval sb_realm;
+ struct berval sb_authcId;
+ struct berval sb_authzId;
+ slap_keepalive sb_keepalive;
+#ifdef HAVE_TLS
+ void *sb_tls_ctx;
+ char *sb_tls_cert;
+ char *sb_tls_key;
+ char *sb_tls_cacert;
+ char *sb_tls_cacertdir;
+ char *sb_tls_reqcert;
+ char *sb_tls_reqsan;
+ char *sb_tls_cipher_suite;
+ char *sb_tls_protocol_min;
+ char *sb_tls_ecname;
+#ifdef HAVE_OPENSSL
+ char *sb_tls_crlcheck;
+#endif
+ int sb_tls_int_reqcert;
+ int sb_tls_int_reqsan;
+ int sb_tls_do_init;
+#endif
+} slap_bindconf;
+
+typedef struct slap_verbmasks {
+ struct berval word;
+ const slap_mask_t mask;
+} slap_verbmasks;
+
+typedef struct slap_cf_aux_table {
+ struct berval key;
+ int off;
+ char type;
+ char quote;
+ void *aux;
+} slap_cf_aux_table;
+
+typedef int slap_cf_aux_table_parse_x( struct berval *val,
+ void *bc,
+ slap_cf_aux_table *tab0,
+ const char *tabmsg,
+ int unparse );
+
+#define SLAP_RESTRICT_OP_ADD 0x0001U
+#define SLAP_RESTRICT_OP_BIND 0x0002U
+#define SLAP_RESTRICT_OP_COMPARE 0x0004U
+#define SLAP_RESTRICT_OP_DELETE 0x0008U
+#define SLAP_RESTRICT_OP_EXTENDED 0x0010U
+#define SLAP_RESTRICT_OP_MODIFY 0x0020U
+#define SLAP_RESTRICT_OP_RENAME 0x0040U
+#define SLAP_RESTRICT_OP_SEARCH 0x0080U
+#define SLAP_RESTRICT_OP_MASK 0x00FFU
+
+#define SLAP_RESTRICT_READONLY 0x80000000U
+
+#define SLAP_RESTRICT_EXOP_START_TLS 0x0100U
+#define SLAP_RESTRICT_EXOP_MODIFY_PASSWD 0x0200U
+#define SLAP_RESTRICT_EXOP_WHOAMI 0x0400U
+#define SLAP_RESTRICT_EXOP_CANCEL 0x0800U
+#define SLAP_RESTRICT_EXOP_MASK 0xFF00U
+
+#define SLAP_RESTRICT_OP_READS \
+ ( SLAP_RESTRICT_OP_COMPARE | SLAP_RESTRICT_OP_SEARCH )
+#define SLAP_RESTRICT_OP_WRITES \
+ ( SLAP_RESTRICT_OP_ADD | SLAP_RESTRICT_OP_DELETE | SLAP_RESTRICT_OP_MODIFY | SLAP_RESTRICT_OP_RENAME )
+#define SLAP_RESTRICT_OP_ALL \
+ ( SLAP_RESTRICT_OP_READS | SLAP_RESTRICT_OP_WRITES | SLAP_RESTRICT_OP_BIND | SLAP_RESTRICT_OP_EXTENDED )
+
+typedef struct config_reply_s ConfigReply; /* config.h */
+
+typedef struct Listener Listener;
+
+/*
+ * represents a connection from an ldap client
+ */
+/* structure state (protected by connections_mutex) */
+enum sc_struct_state {
+ SLAP_C_UNINITIALIZED = 0, /* MUST BE ZERO (0) */
+ SLAP_C_UNUSED,
+ SLAP_C_USED,
+ SLAP_C_PENDING
+};
+
+/* connection state (protected by c_mutex) */
+enum sc_conn_state {
+ SLAP_C_INVALID = 0, /* MUST BE ZERO (0) */
+ SLAP_C_INACTIVE, /* zero threads */
+ SLAP_C_CLOSING, /* closing */
+ SLAP_C_ACTIVE, /* one or more threads */
+ SLAP_C_BINDING, /* binding */
+ SLAP_C_CLIENT /* outbound client conn */
+};
+struct Connection {
+ enum sc_struct_state c_struct_state; /* structure management state */
+ enum sc_conn_state c_conn_state; /* connection state */
+ ber_socket_t c_sd;
+
+ ldap_pvt_thread_mutex_t c_mutex; /* protect the connection */
+ Sockbuf *c_sb; /* ber connection stuff */
+
+ /* set by connection_init */
+ unsigned long c_connid; /* unique id of this connection */
+ struct berval c_peer_name; /* peer name (trans=addr:port) */
+ time_t c_starttime; /* when the connection was opened */
+
+ time_t c_activitytime; /* when the connection was last used */
+
+ struct event *c_read_event, *c_write_event;
+
+ /* can only be changed by binding thread */
+ struct berval c_sasl_bind_mech; /* mech in progress */
+
+ ldap_pvt_thread_mutex_t c_io_mutex; /* only one pdu written at a time */
+
+ BerElement *c_currentber; /* ber we're attempting to read */
+ struct berval c_pendingber; /* ber we're attempting to write */
+
+#define CONN_IS_TLS 1
+#define CONN_IS_CLIENT 4
+#define CONN_IS_IPC 8
+
+#ifdef HAVE_TLS
+ char c_is_tls; /* true if this LDAP over raw TLS */
+ char c_needs_tls_accept; /* true if SSL_accept should be called */
+#endif
+
+ long c_n_ops_executing; /* num of ops currently executing */
+ long c_n_ops_completed; /* num of ops completed */
+
+ void *c_private;
+};
+
+#ifdef LDAP_DEBUG
+#ifdef LDAP_SYSLOG
+#ifdef LOG_LOCAL4
+#define SLAP_DEFAULT_SYSLOG_USER LOG_LOCAL4
+#endif /* LOG_LOCAL4 */
+
+#define Statslog( level, fmt, connid, opid, arg1, arg2, arg3 ) \
+ Log( (level), ldap_syslog_level, (fmt), (connid), (opid), \
+ ( arg1 ), ( arg2 ), ( arg3 ) )
+#define StatslogTest( level ) ( ( ldap_debug | ldap_syslog ) & ( level ) )
+#else /* !LDAP_SYSLOG */
+#define Statslog( level, fmt, connid, opid, arg1, arg2, arg3 ) \
+ do { \
+ if ( ldap_debug & (level) ) \
+ lutil_debug( ldap_debug, (level), (fmt), (connid), (opid), \
+ ( arg1 ), ( arg2 ), ( arg3 ) ); \
+ } while (0)
+#define StatslogTest( level ) ( ldap_debug & ( level ) )
+#endif /* !LDAP_SYSLOG */
+#else /* !LDAP_DEBUG */
+#define Statslog( level, fmt, connid, opid, arg1, arg2, arg3 ) ( (void)0 )
+#define StatslogTest( level ) ( 0 )
+#endif /* !LDAP_DEBUG */
+
+/*
+ * listener; need to access it from monitor backend
+ */
+struct Listener {
+ struct berval sl_url;
+ struct berval sl_name;
+ mode_t sl_perms;
+#ifdef HAVE_TLS
+ int sl_is_tls;
+#endif
+ struct event_base *base;
+ struct evconnlistener *listener;
+ int sl_mute; /* Listener is temporarily disabled due to emfile */
+ int sl_busy; /* Listener is busy (accept thread activated) */
+ ber_socket_t sl_sd;
+ Sockaddr sl_sa;
+#define sl_addr sl_sa.sa_in_addr
+#define LDAP_TCP_BUFFER
+#ifdef LDAP_TCP_BUFFER
+ int sl_tcp_rmem; /* custom TCP read buffer size */
+ int sl_tcp_wmem; /* custom TCP write buffer size */
+#endif
+};
+
+LDAP_END_DECL
+
+#include "proto-slap.h"
+
+#endif /* _SLAP_H_ */
--- /dev/null
+../slapd/user.c
\ No newline at end of file
--- /dev/null
+/* value.c - routines for dealing with values */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2015 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/*
+ * Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#include "portable.h"
+
+#include "slap.h"
+
+int
+value_add_one( BerVarray *vals, struct berval *addval )
+{
+ int n;
+ BerVarray v2;
+
+ if ( *vals == NULL ) {
+ *vals = (BerVarray)SLAP_MALLOC( 2 * sizeof(struct berval) );
+ if ( *vals == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "value_add_one: "
+ "SLAP_MALLOC failed.\n" );
+ return LBER_ERROR_MEMORY;
+ }
+ n = 0;
+
+ } else {
+ for ( n = 0; !BER_BVISNULL( &(*vals)[n] ); n++ ) {
+ ; /* Empty */
+ }
+ *vals = (BerVarray)SLAP_REALLOC(
+ (char *)*vals, ( n + 2 ) * sizeof(struct berval) );
+ if ( *vals == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "value_add_one: "
+ "SLAP_MALLOC failed.\n" );
+ return LBER_ERROR_MEMORY;
+ }
+ }
+
+ v2 = &(*vals)[n];
+ ber_dupbv( v2, addval );
+
+ v2++;
+ BER_BVZERO( v2 );
+
+ return LDAP_SUCCESS;
+}
* OLcfg{Bk|Db}{Oc|At}:10 -> back-shell
* OLcfg{Bk|Db}{Oc|At}:11 -> back-perl
* OLcfg{Bk|Db}{Oc|At}:12 -> back-mdb
+ * OLcfg{Bk|Db}{Oc|At}:13 -> lloadd
*/
/*