diff options
Diffstat (limited to 'ldap/servers/slapd/daemon.c')
-rw-r--r-- | ldap/servers/slapd/daemon.c | 2694 |
1 files changed, 2694 insertions, 0 deletions
diff --git a/ldap/servers/slapd/daemon.c b/ldap/servers/slapd/daemon.c new file mode 100644 index 00000000..db79288f --- /dev/null +++ b/ldap/servers/slapd/daemon.c @@ -0,0 +1,2694 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +#include <string.h> +#include <sys/types.h> +#ifdef _WIN32 +#include <windows.h> +#include <process.h> /* for getpid */ +#include "proto-ntutil.h" +#include "ntslapdmessages.h" +#else +#include <unistd.h> +#include <sys/socket.h> +#include <errno.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <sys/types.h> +#include <netinet/tcp.h> +#include <netdb.h> +#include <sys/time.h> +#include <sys/wait.h> +#include <pthread.h> +#endif +#include <time.h> +#include <signal.h> +#if defined(IRIX6_2) || defined(IRIX6_3) +#include <sys/param.h> +#endif +#if defined(_AIX) +#include <sys/select.h> +#include <sys/param.h> +#endif +#include <fcntl.h> +#define TCPLEN_T int +#if !defined( _WIN32 ) +#ifdef NEED_FILIO +#include <sys/filio.h> +#else /* NEED_FILIO */ +#include <sys/ioctl.h> +#endif /* NEED_FILIO */ +#endif /* !defined( _WIN32 ) */ +/* for some reason, linux tty stuff defines CTIME */ +#ifdef LINUX +#undef CTIME +#endif +#include "slap.h" +#include "slapi-plugin.h" + +#include "snmp_collator.h" +#include <private/pprio.h> + +#if defined( NET_SSL ) +#include <ssl.h> +#endif /* defined(NET_SSL) */ + +#include "fe.h" + +/* + * Define the backlog number for use in listen() call. + * We use the same definition as in ldapserver/include/base/systems.h + */ +#ifndef DAEMON_LISTEN_SIZE +#define DAEMON_LISTEN_SIZE 128 +#endif + +#if defined (LDAP_IOCP) +#define SLAPD_WAKEUP_TIMER 250 +#else +#define SLAPD_WAKEUP_TIMER 250 +#endif + +int slapd_wakeup_timer = SLAPD_WAKEUP_TIMER; /* time in ms to wakeup */ +#ifdef notdef /* GGOODREPL */ +/* + * time in secs to do housekeeping: + * this must be greater than slapd_wakeup_timer + */ +short slapd_housekeeping_timer = 10; +#endif /* notdef GGOODREPL */ + +/* Do we support timeout on socket send() ? */ +int have_send_timeouts = 0; + +PRFileDesc* signalpipe[2]; +static int writesignalpipe = SLAPD_INVALID_SOCKET; +static int readsignalpipe = SLAPD_INVALID_SOCKET; + +#define FDS_SIGNAL_PIPE 0 +#define FDS_N_TCPS 1 +#define FDS_S_TCPS 2 + +static int get_configured_connection_table_size(); +#ifdef RESOLVER_NEEDS_LOW_FILE_DESCRIPTORS +static void get_loopback_by_addr( void ); +#endif + +#ifdef XP_WIN32 +static int createlistensocket(unsigned short port, const PRNetAddr *listenaddr); +#endif +static PRFileDesc *createprlistensocket(unsigned short port, + const PRNetAddr *listenaddr, int secure); +static const char *netaddr2string(const PRNetAddr *addr, char *addrbuf, + size_t addrbuflen); +static void set_shutdown (int); +static void setup_pr_read_pds(Connection_Table *ct, PRFileDesc *n_tcps, PRFileDesc *s_tcps, PRIntn *num_to_read); + +#ifdef HPUX10 +static void* catch_signals(); +#endif + +#if defined( _WIN32 ) +HANDLE hServDoneEvent = NULL; +#endif + +static int createsignalpipe( void ); + + +#if defined( _WIN32 ) +/* Set an event to hook the NT Service termination */ +void *slapd_service_exit_wait() +{ +#if defined( PURIFYING ) + +#include <sys/types.h> +#include <sys/stat.h> + + char module[_MAX_FNAME]; + char exit_file_name[_MAX_FNAME]; + char drive[_MAX_DRIVE]; + char dir[_MAX_DIR]; + char fname[_MAX_FNAME]; + char ext[_MAX_EXT]; + struct stat statbuf; + + memset( module, 0, sizeof( module ) ); + memset( exit_file_name, 0, sizeof( exit_file_name ) ); + + GetModuleFileName(GetModuleHandle( NULL ), module, sizeof( module ) ); + + _splitpath( module, drive, dir, fname, ext ); + + sprintf( exit_file_name, "%s%s%s", drive, dir, "exitnow.txt" ); + + LDAPDebug( LDAP_DEBUG_ANY, "PURIFYING - Create %s to terminate the process.\n", exit_file_name, 0, 0 ); + + while ( TRUE ) + { + if( stat( exit_file_name, &statbuf ) < 0) + { + Sleep( 5000 ); /* 5 Seconds */ + continue; + } + LDAPDebug( LDAP_DEBUG_ANY, "slapd shutting down immediately, " + "\"%s\" exists - don't forget to delete it\n", exit_file_name, 0, 0 ); + g_set_shutdown( SLAPI_SHUTDOWN_SIGNAL ); + return NULL; + } + +#else /* PURIFYING */ + + DWORD dwWait; + char szDoneEvent[256]; + + sprintf(szDoneEvent, "NS_%s", pszServerName); + + hServDoneEvent = CreateEvent( NULL, // default security attributes (LocalSystem) + TRUE, // manual reset event + FALSE, // not-signalled + szDoneEvent );// named after the service itself. + + /* Wait indefinitely until hServDoneEvent is signaled. */ + dwWait = WaitForSingleObject( hServDoneEvent, // event object + INFINITE ); // wait indefinitely + + /* The termination event has been signalled, log this occurrence, and signal to exit. */ + ReportSlapdEvent( EVENTLOG_INFORMATION_TYPE, MSG_SERVER_SHUTDOWN_STARTING, 0, NULL ); + + g_set_shutdown( SLAPI_SHUTDOWN_SIGNAL ); + return NULL; +#endif /* PURIFYING */ +} +#endif /* _WIN32 */ + +static char * +get_pid_file() +{ + return(pid_file); +} + +static int daemon_configure_send_timeout(int s,size_t timeout /* Miliseconds*/) +{ + /* Currently this function is only good for NT, and expects the s argument to be a SOCKET */ +#if defined(_WIN32) + return setsockopt( + s, + SOL_SOCKET, + SO_SNDTIMEO, + (char*) &timeout, + sizeof(timeout) + ); +#else + return 0; +#endif +} + +#if defined (_WIN32) +/* This function is a workaround for accept problem on NT. + Accept call fires on NT during syn scan even though the connection is not + open. This causes a resource leak. For more details, see bug 391414. + Experimentally, we determined that, in case of syn scan, the local + address is set to 0. This in undocumented and my change in the future + + The function returns 0 if this is normal connection + 1 if this is syn_scan connection + -1 in case of any other error + */ +static int +syn_scan (int sock) +{ + int rc; + struct sockaddr_in addr; + int size = sizeof (addr); + + if (sock == SLAPD_INVALID_SOCKET) + return -1; + + rc = getsockname (sock, (struct sockaddr*)&addr, &size); + if (rc != 0) + return -1; + else if (addr.sin_addr.s_addr == 0) + return 1; + else + return 0; +} + +#endif + +static int +accept_and_configure(int s, PRFileDesc *pr_acceptfd, PRNetAddr *pr_netaddr, + int addrlen, int secure, PRFileDesc **pr_clonefd) +{ + int ns = 0; + int ioblock_timeout = config_get_ioblocktimeout(); + int enable_nagle = config_get_nagle(); + + PRIntervalTime pr_timeout = PR_MillisecondsToInterval(slapd_wakeup_timer); + +#if !defined( XP_WIN32 ) + (*pr_clonefd) = PR_Accept(pr_acceptfd, pr_netaddr, pr_timeout); + if( !(*pr_clonefd) ) { + PRErrorCode prerr = PR_GetError(); + LDAPDebug( LDAP_DEBUG_ANY, "PR_Accept() failed, " + SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n", + prerr, slapd_pr_strerror(prerr), 0 ); + return(SLAPD_INVALID_SOCKET); + } + + ns = configure_pr_socket( pr_clonefd, secure ); + +#else + if( secure ) { + (*pr_clonefd) = PR_Accept(pr_acceptfd, pr_netaddr, pr_timeout); + if( !(*pr_clonefd) ) { + PRErrorCode prerr = PR_GetError(); + LDAPDebug( LDAP_DEBUG_ANY, "PR_Accept() failed, " + SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n", + prerr, slapd_pr_strerror(prerr), 0 ); + + /* Bug 613324: Call PR_NT_CancelIo if an error occurs */ + if( (prerr == PR_IO_TIMEOUT_ERROR ) || + (prerr == PR_PENDING_INTERRUPT_ERROR) ) { + if( (PR_NT_CancelIo( pr_acceptfd )) != PR_SUCCESS) { + prerr = PR_GetError(); + LDAPDebug( LDAP_DEBUG_ANY, + "PR_NT_CancelIo() failed, " + SLAPI_COMPONENT_NAME_NSPR + " error %d (%s)\n", + prerr, slapd_pr_strerror(prerr), 0 ); + } + } + return(SLAPD_INVALID_SOCKET); + } + + ns = configure_pr_socket( pr_clonefd, secure ); + + } else { + struct sockaddr *addr; + + addr = (struct sockaddr *) slapi_ch_malloc( sizeof(struct sockaddr) ); + ns = accept (s, addr, (TCPLEN_T *)&addrlen); + + if (ns == SLAPD_INVALID_SOCKET) { + int oserr = errno; + + LDAPDebug( LDAP_DEBUG_ANY, + "accept(%d) failed errno %d (%s)\n", + s, oserr, slapd_system_strerror(oserr)); + } + + else if (syn_scan (ns)) + { + /* this is a work around for accept problem with SYN scan on NT. + See bug 391414 for more details */ + LDAPDebug(LDAP_DEBUG_ANY, "syn-scan request is received - ignored\n", 0, 0, 0); + closesocket (ns); + ns = SLAPD_INVALID_SOCKET; + } + + if ( PR_SetNetAddr(PR_IpAddrNull, PR_AF_INET6, ((struct sockaddr_in *)addr)->sin_port, pr_netaddr) + != PR_SUCCESS ) { + int oserr = PR_GetError(); + LDAPDebug( LDAP_DEBUG_ANY, "PR_SetNetAddr() failed, " + SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n", + oserr, slapd_pr_strerror(oserr), 0 ); + } else { + PR_ConvertIPv4AddrToIPv6(((struct sockaddr_in *)addr)->sin_addr.s_addr, &(pr_netaddr->ipv6.ip)); + } + + (*pr_clonefd) = NULL; + + slapi_ch_free( (void **)&addr ); + configure_ns_socket( &ns ); + } +#endif + + return ns; +} + +/* + * This is the shiny new re-born daemon function, without all the hair + */ +#ifdef _WIN32 +static void setup_read_fds(Connection_Table *ct, fd_set *readfds, int n_tcps, int s_tcps ); +static void handle_read_ready(Connection_Table *ct, fd_set *readfds); +static void set_timeval_ms(struct timeval *t, int ms); +#endif +/* GGOODREPL static void handle_timeout( void ); */ +static void handle_pr_read_ready(Connection_Table *ct, PRIntn num_poll); +static int handle_new_connection(Connection_Table *ct, int tcps, PRFileDesc *pr_acceptfd, int secure ); +#ifdef _WIN32 +static void unfurl_banners(Connection_Table *ct,daemon_ports_t *ports, int n_tcps, PRFileDesc *s_tcps); +#else +static void unfurl_banners(Connection_Table *ct,daemon_ports_t *ports, PRFileDesc *n_tcps, PRFileDesc *s_tcps); +#endif +static int write_pid_file(); +static int init_shutdown_detect(); +#ifdef _WIN32 +static int clear_signal(fd_set *readfdset); +#else +static int clear_signal(struct POLL_STRUCT *fds); +#endif + +/* Globals which are used to store the sockets between + * calls to daemon_pre_setuid_init() and the daemon thread + * creation. */ + +int daemon_pre_setuid_init(daemon_ports_t *ports) +{ + int rc = 0; + + if (0 != ports->n_port) { +#if defined( XP_WIN32 ) + ports->n_socket = createlistensocket((unsigned short)ports->n_port, + &ports->n_listenaddr); +#else + ports->n_socket = createprlistensocket(ports->n_port, + &ports->n_listenaddr, 0); +#endif + } + + if ( config_get_security() && (0 != ports->s_port) ) { + ports->s_socket = createprlistensocket((unsigned short)ports->s_port, + &ports->s_listenaddr, 1); +#ifdef XP_WIN32 + ports->s_socket_native = PR_FileDesc2NativeHandle(ports->s_socket); +#endif + /* check if ports->s_socket != -1 ? */ + rc = slapd_ssl_init2 ( &ports->s_socket, 0 ); + } else { + ports->s_socket = SLAPD_INVALID_SOCKET; +#ifdef XP_WIN32 + ports->s_socket_native = SLAPD_INVALID_SOCKET; +#endif + } + + return( rc ); +} + + +/* Decide whether we're running on a platform which supports send with timeouts */ +static void detect_timeout_support() +{ + /* Currently we know that NT4.0 or higher DOES support timeouts */ +#if defined _WIN32 + /* Get the OS revision */ + OSVERSIONINFO ver; + ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx(&ver); + if (ver.dwPlatformId == VER_PLATFORM_WIN32_NT && ver.dwMajorVersion >= 4) { + have_send_timeouts = 1; + } +#else + /* Some UNIXen do, but for now I don't feel confident which , and whether timeouts really work there */ +#endif +} + + +/* + * The time_shutdown static variable is used to signal the time thread + * to shutdown. We used to shut down the time thread when g_get_shutdown() + * returned a non-zero value, but that caused the clock to stop, so to speak, + * and all error log entries to have the same timestamp once the shutdown + * process began. + */ +static int time_shutdown = 0; + +void * +time_thread(void *nothing) +{ + PRIntervalTime interval; + + interval = PR_SecondsToInterval(1); + + while(!time_shutdown) { + poll_current_time(); + csngen_update_time (); + DS_Sleep(interval); + } + + /*NOTREACHED*/ + return(NULL); +} + + +void slapd_daemon( daemon_ports_t *ports ) +{ + /* We are passed a pair of ports---one for regular connections, the + * other for SSL connections. + */ + /* Previously there was a ton of code #defined on NET_SSL. + * This looked horrible, so now I'm doing it this way: + * If you want me to do SSL, pass me something in the ssl port number. + * If you don't, pass me zero. + */ + +#if defined( XP_WIN32 ) + int n_tcps = 0; + int s_tcps_native = 0; +#else + PRFileDesc *n_tcps = NULL; + PRFileDesc *tcps = 0; +#endif + PRFileDesc *s_tcps = NULL; + PRIntn num_poll = 0; + PRIntervalTime pr_timeout = PR_MillisecondsToInterval(slapd_wakeup_timer); + PRThread *time_thread_p; + int threads; + int in_referral_mode = config_check_referral_mode(); + + int connection_table_size = get_configured_connection_table_size(); + the_connection_table= connection_table_new(connection_table_size); + +#ifdef RESOLVER_NEEDS_LOW_FILE_DESCRIPTORS + /* + * Some DNS resolver implementations, such as the one built into + * Solaris <= 8, need to use one or more low numbered file + * descriptors internally (probably because they use a deficient + * implementation of stdio). So we make a call now that uses the + * resolver so it has an opportunity to grab whatever low file + * descriptors it needs (before we use up all of the low numbered + * ones for incoming client connections and so on). + */ + get_loopback_by_addr(); +#endif + + /* Retrieve the sockets from their hiding place */ + n_tcps = ports->n_socket; + s_tcps = ports->s_socket; +#ifdef XP_WIN32 + s_tcps_native = ports->s_socket_native; +#endif + + createsignalpipe(); + + init_shutdown_detect(); + +#if defined( XP_WIN32 ) + if ( (n_tcps == SLAPD_INVALID_SOCKET) && +#else + if ( (n_tcps == NULL) && +#endif + (s_tcps == NULL) ) { /* nothing to do */ + LDAPDebug( LDAP_DEBUG_ANY, + "no port to listen on\n", 0, 0, 0 ); + exit( 1 ); + } + + unfurl_banners(the_connection_table,ports,n_tcps,s_tcps); + init_op_threads (); + detect_timeout_support(); + + /* Start the time thread */ + time_thread_p = PR_CreateThread(PR_SYSTEM_THREAD, + (VFP) (void *) time_thread, NULL, + PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, + PR_JOINABLE_THREAD, + SLAPD_DEFAULT_THREAD_STACKSIZE); + if ( NULL == time_thread_p ) { + PRErrorCode errorCode = PR_GetError(); + LDAPDebug(LDAP_DEBUG_ANY, "Unable to create time thread - Shutting Down (" + SLAPI_COMPONENT_NAME_NSPR " error %d - %s)\n", + errorCode, slapd_pr_strerror(errorCode), 0); + g_set_shutdown( SLAPI_SHUTDOWN_EXIT ); + } + + /* We are now ready to accept imcoming connections */ +#if defined( XP_WIN32 ) + if ( n_tcps != SLAPD_INVALID_SOCKET + && listen( n_tcps, DAEMON_LISTEN_SIZE ) == -1 ) { + int oserr = errno; + char addrbuf[ 256 ]; + + slapi_log_error(SLAPI_LOG_FATAL, "slapd_daemon", + "listen() on %s port %d failed: OS error %d (%s)\n", + netaddr2string(&ports->n_listenaddr, addrbuf, sizeof(addrbuf)), + ports->n_port, oserr, slapd_system_strerror( oserr ) ); + g_set_shutdown( SLAPI_SHUTDOWN_EXIT ); + } +#else + if ( n_tcps != NULL + && PR_Listen( n_tcps, DAEMON_LISTEN_SIZE ) == PR_FAILURE) { + PRErrorCode prerr = PR_GetError(); + char addrbuf[ 256 ]; + + slapi_log_error(SLAPI_LOG_FATAL, "slapd_daemon", + "PR_Listen() on %s port %d failed: %s error %d (%s)\n", + netaddr2string(&ports->n_listenaddr, addrbuf, sizeof(addrbuf)), + ports->n_port, SLAPI_COMPONENT_NAME_NSPR, prerr, + slapd_pr_strerror( prerr )); + g_set_shutdown( SLAPI_SHUTDOWN_EXIT ); + } +#endif + + if ( s_tcps != NULL + && PR_Listen( s_tcps, DAEMON_LISTEN_SIZE ) == PR_FAILURE ) { + PRErrorCode prerr = PR_GetError(); + char addrbuf[ 256 ]; + + slapi_log_error(SLAPI_LOG_FATAL, "slapd_daemon", + "PR_Listen() on %s port %d failed: %s error %d (%s)\n", + netaddr2string(&ports->s_listenaddr, addrbuf, sizeof(addrbuf)), + ports->s_port, SLAPI_COMPONENT_NAME_NSPR, prerr, + slapd_pr_strerror( prerr )); + g_set_shutdown( SLAPI_SHUTDOWN_EXIT ); + } + + /* Now we write the pid file, indicating that the server is finally and listening for connections */ + write_pid_file(); + + /* The meat of the operation is in a loop on a call to select */ + while(!g_get_shutdown()) + { +#ifdef _WIN32 + fd_set readfds; + struct timeval wakeup_timer; + int oserr; +#endif + int select_return = 0; + int secure = 0; /* is a new connection an SSL one ? */ +#ifndef _WIN32 + PRErrorCode prerr; +#endif + +#ifdef _WIN32 + set_timeval_ms(&wakeup_timer, slapd_wakeup_timer); + setup_read_fds(the_connection_table,&readfds,n_tcps, s_tcps_native); + /* This select needs to timeout to give the server a chance to test for shutdown */ + select_return = select(connection_table_size, &readfds, NULL, 0, &wakeup_timer); +#else + setup_pr_read_pds(the_connection_table,n_tcps,s_tcps,&num_poll); + select_return = POLL_FN(the_connection_table->fd, num_poll, pr_timeout); +#endif + switch (select_return) { + case 0: /* Timeout */ + /* GGOODREPL handle_timeout(); */ + break; + case -1: /* Error */ +#ifdef _WIN32 + oserr = errno; + + LDAPDebug( LDAP_DEBUG_TRACE, + "select failed errno %d (%s)\n", oserr, + slapd_system_strerror(oserr), 0 ); +#else + prerr = PR_GetError(); + LDAPDebug( LDAP_DEBUG_TRACE, "PR_Poll() failed, " + SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n", + prerr, slapd_system_strerror(prerr), 0 ); +#endif + break; + default: /* either a new connection or some new data ready */ + /* Figure out if we are dealing with one of the listen sockets */ +#ifdef _WIN32 + /* If so, then handle a new connection */ + if ( n_tcps != SLAPD_INVALID_SOCKET && FD_ISSET( n_tcps,&readfds ) ) { + handle_new_connection(the_connection_table,n_tcps,NULL,0); + } + /* If so, then handle a new connection */ + if ( s_tcps != SLAPD_INVALID_SOCKET && FD_ISSET( s_tcps_native,&readfds ) ) { + handle_new_connection(the_connection_table,SLAPD_INVALID_SOCKET,s_tcps,1); + } + /* handle new data ready */ + handle_read_ready(the_connection_table,&readfds); + clear_signal(&readfds); +#else + tcps = NULL; + /* info for n_tcps is always in fd[FDS_N_TCPS] and info for s_tcps is always + * in fd[FDS_S_TCPS] */ + if( n_tcps != NULL && + the_connection_table->fd[FDS_N_TCPS].out_flags & SLAPD_POLL_FLAGS ) { + tcps = n_tcps; + } else if ( s_tcps != NULL && + the_connection_table->fd[FDS_S_TCPS].out_flags & SLAPD_POLL_FLAGS ) { + tcps = s_tcps; + secure = 1; + } + /* If so, then handle a new connection */ + if ( tcps != NULL ) { + handle_new_connection(the_connection_table,SLAPD_INVALID_SOCKET,tcps,secure); + } + /* handle new data ready */ + handle_pr_read_ready(the_connection_table, connection_table_size); + clear_signal(the_connection_table->fd); +#endif + break; + } + + } + /* We get here when the server is shutting down */ + /* Do what we have to do before death */ + + connection_table_abandon_all_operations(the_connection_table); /* abandon all operations in progress */ + + if ( ! in_referral_mode ) { + ps_stop_psearch_system(); /* stop any persistent searches */ + } + +#ifdef _WIN32 + if ( n_tcps != SLAPD_INVALID_SOCKET ) { + closesocket( n_tcps ); +#else + if ( n_tcps != NULL ) { + PR_Close( n_tcps ); +#endif + } + if ( s_tcps != NULL ) { + PR_Close( s_tcps ); + } + + /* Might compete with housecleaning thread, but so far so good */ + be_flushall(); + op_thread_cleanup(); + housekeeping_stop(); /* Run this after op_thread_cleanup() logged sth */ + +#ifndef _WIN32 + if ( active_threads > 0 ) { + LDAPDebug( LDAP_DEBUG_ANY, + "slapd shutting down - waiting for %d thread%s to terminate\n", + active_threads, ( active_threads > 1 ) ? "s" : "", 0 ); + } +#endif + + threads = active_threads; + while ( active_threads > 0 ) { + PRPollDesc xpd; + char x; + int spe = 0; + + /* try to read from the signal pipe, in case threads are + * blocked on it. */ + xpd.fd = signalpipe[0]; + xpd.in_flags = PR_POLL_READ; + xpd.out_flags = 0; + spe = PR_Poll(&xpd, 1, PR_INTERVAL_NO_WAIT); + if (spe > 0) { + spe = PR_Read(signalpipe[0], &x, 1); + if (spe < 0) { + PRErrorCode prerr = PR_GetError(); + LDAPDebug( LDAP_DEBUG_ANY, "listener could not clear signal pipe, " + SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n", + prerr, slapd_system_strerror(prerr), 0 ); + break; + } + } else if (spe == -1) { + PRErrorCode prerr = PR_GetError(); + LDAPDebug( LDAP_DEBUG_ANY, "PR_Poll() failed, " + SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n", + prerr, slapd_system_strerror(prerr), 0 ); + break; + } else { + /* no data */ + } + DS_Sleep(PR_INTERVAL_NO_WAIT); + if ( threads != active_threads ) { + LDAPDebug( LDAP_DEBUG_TRACE, + "slapd shutting down - waiting for %d threads to terminate\n", + active_threads, 0, 0 ); + threads = active_threads; + } + } + + LDAPDebug( LDAP_DEBUG_ANY, + "slapd shutting down - closing down internal subsystems and plugins\n", + 0, 0, 0 ); + + log_access_flush(); + + /* let backends do whatever cleanup they need to do */ + LDAPDebug( LDAP_DEBUG_TRACE,"slapd shutting down - waiting for backends to close down\n", 0, 0,0 ); + + eq_stop(); + if ( ! in_referral_mode ) { + task_shutdown(); + uniqueIDGenCleanup (); + } + + plugin_closeall( 1 /* Close Backends */, 1 /* Close Gloabls */); + + if ( ! in_referral_mode ) { + /* Close SNMP collator after the plugins closed... + * Replication plugin still performs internal ops that + * may try to increment snmp stats. + * Fix for defect 523780 + */ + snmp_collator_stop(); + mapping_tree_free (); + } + + be_cleanupall (); + LDAPDebug( LDAP_DEBUG_TRACE, "slapd shutting down - backends closed down\n", + 0, 0, 0 ); + referrals_free(); + + connection_table_free(the_connection_table); + the_connection_table= NULL; + + /* tell the time thread to shutdown and then wait for it */ + time_shutdown = 1; + PR_JoinThread( time_thread_p ); + +#ifdef _WIN32 + WSACleanup(); +#endif +} + +int signal_listner() +{ + /* Replaces previous macro---called to bump the thread out of select */ +#if defined( _WIN32 ) + if ( PR_Write( signalpipe[1], "", 1) != 1 ) { + /* this now means that the pipe is full + * this is not a problem just go-on + */ + LDAPDebug( LDAP_DEBUG_CONNS, + "listener could not write to signal pipe %d\n", + errno, 0, 0 ); + } + +#else + if ( write( writesignalpipe, "", 1) != 1 ) { + /* this now means that the pipe is full + * this is not a problem just go-on + */ + LDAPDebug( LDAP_DEBUG_CONNS, + "listener could not write to signal pipe %d\n", + errno, 0, 0 ); + } +#endif + return( 0 ); +} + +#ifdef _WIN32 +static int clear_signal(fd_set *readfdset) +#else +static int clear_signal(struct POLL_STRUCT *fds) +#endif +{ +#ifdef _WIN32 + if ( FD_ISSET(readsignalpipe, readfdset)) { +#else + if ( fds[FDS_SIGNAL_PIPE].out_flags & SLAPD_POLL_FLAGS ) { +#endif + char buf[200]; + + LDAPDebug( LDAP_DEBUG_CONNS, + "listener got signaled\n", + 0, 0, 0 ); +#ifdef _WIN32 + if ( PR_Read( signalpipe[0], buf, 20 ) < 1 ) { +#else + if ( read( readsignalpipe, buf, 200 ) < 1 ) { +#endif + LDAPDebug( LDAP_DEBUG_ANY, + "listener could not clear signal pipe\n", + 0, 0, 0 ); + } + } + return 0; +} + +#ifdef _WIN32 +static void set_timeval_ms(struct timeval *t, int ms) +{ + t->tv_sec = ms/1000; + t->tv_usec = (ms % 1000)*1000; +} +#endif + +#ifdef _WIN32 +static void setup_read_fds(Connection_Table *ct, fd_set *readfds, int n_tcps, int s_tcps) +{ + Connection *c= NULL; + Connection *next= NULL; + int accept_new_connections; + static int last_accept_new_connections = -1; + slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig(); + + LBER_SOCKET socketdesc = SLAPD_INVALID_SOCKET; + + FD_ZERO( readfds ); + + accept_new_connections = ((ct->size - g_get_current_conn_count()) + > slapdFrontendConfig->reservedescriptors); + if ( ! accept_new_connections ) { + if ( last_accept_new_connections ) { + LDAPDebug( LDAP_DEBUG_ANY, "Not listening for new " + "connections - too many fds open\n", 0, 0, 0 ); + } + } else { + if ( ! last_accept_new_connections && + last_accept_new_connections != -1 ) { + LDAPDebug( LDAP_DEBUG_ANY, "Listening for new " + "connections again\n", 0, 0, 0 ); + } + } + last_accept_new_connections = accept_new_connections; + if (n_tcps != SLAPD_INVALID_SOCKET && accept_new_connections) { + FD_SET( n_tcps, readfds ); + LDAPDebug( LDAP_DEBUG_HOUSE, + "listening for connections on %d\n", n_tcps, 0, 0 ); + } + if (s_tcps != SLAPD_INVALID_SOCKET && accept_new_connections) { + FD_SET( s_tcps, readfds ); + LDAPDebug( LDAP_DEBUG_HOUSE, + "listening for connections on %d\n", s_tcps, 0, 0 ); + } + + if ((s_tcps != SLAPD_INVALID_SOCKET) + && (readsignalpipe != SLAPD_INVALID_SOCKET)) { + FD_SET( readsignalpipe, readfds ); + } + + /* Walk down the list of active connections to find + * out which connections we should poll over. If a connection + * is no longer in use, we should remove it from the linked + * list. */ + c= connection_table_get_first_active_connection (ct); + while (c) + { + next = connection_table_get_next_active_connection (ct, c); + if ( c->c_mutex == NULL ) + { + connection_table_move_connection_out_of_active_list(ct,c); + } + else + { + PR_Lock( c->c_mutex ); + if ( c->c_flags & CONN_FLAG_CLOSING ) + { + /* A worker thread has marked that this connection + * should be closed by calling disconnect_server. + * move this connection out of the active list + * the last thread to use the connection will close it + */ + connection_table_move_connection_out_of_active_list(ct,c); + } + else if ( c->c_sd == SLAPD_INVALID_SOCKET ) + { + connection_table_move_connection_out_of_active_list(ct,c); + } + else + { +#if defined(LDAP_IOCP) /* When we have IO completion ports, we don't want to do this */ + if ( !c->c_gettingber && (c->c_flags & CONN_FLAG_SSL) ) +#else + if ( !c->c_gettingber ) +#endif + { + FD_SET( c->c_sd, readfds ); + } + } + PR_Unlock( c->c_mutex ); + } + c = next; + } +} +#endif /* _WIN32 */ + +static int first_time_setup_pr_read_pds = 1; +static void +setup_pr_read_pds(Connection_Table *ct, PRFileDesc *n_tcps, PRFileDesc *s_tcps, PRIntn *num_to_read) +{ + Connection *c= NULL; + Connection *next= NULL; + LBER_SOCKET socketdesc = SLAPD_INVALID_SOCKET; + int accept_new_connections; + static int last_accept_new_connections = -1; + PRIntn count = 0; + slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig(); + int max_threads_per_conn = config_get_maxthreadsperconn(); + + accept_new_connections = ((ct->size - g_get_current_conn_count()) + > slapdFrontendConfig->reservedescriptors); + if ( ! accept_new_connections ) { + if ( last_accept_new_connections ) { + LDAPDebug( LDAP_DEBUG_ANY, "Not listening for new " + "connections - too many fds open\n", 0, 0, 0 ); + /* reinitialize n_tcps and s_tcps to the pds */ + first_time_setup_pr_read_pds = 1; + } + } else { + if ( ! last_accept_new_connections && + last_accept_new_connections != -1 ) { + LDAPDebug( LDAP_DEBUG_ANY, "Listening for new " + "connections again\n", 0, 0, 0 ); + /* reinitialize n_tcps and s_tcps to the pds */ + first_time_setup_pr_read_pds = 1; + } + } + last_accept_new_connections = accept_new_connections; + + + /* initialize the mapping from connection table entries to fds entries */ + if (first_time_setup_pr_read_pds) + { + int i; + for (i = 0; i < ct->size; i++) + { + ct->c[i].c_fdi = SLAPD_INVALID_SOCKET_INDEX; + } + + /* The fds entry for n_tcps is always FDS_N_TCPS */ + if (n_tcps != NULL && accept_new_connections) + { + ct->fd[FDS_N_TCPS].fd = n_tcps; + ct->fd[FDS_N_TCPS].in_flags = SLAPD_POLL_FLAGS; + ct->fd[FDS_N_TCPS].out_flags = 0; + LDAPDebug( LDAP_DEBUG_HOUSE, + "listening for connections on %d\n", socketdesc, 0, 0 ); + } else { + ct->fd[FDS_N_TCPS].fd = NULL; + } + + /* The fds entry for s_tcps is always FDS_S_TCPS */ + if (s_tcps != NULL && accept_new_connections) + { + ct->fd[FDS_S_TCPS].fd = s_tcps; + ct->fd[FDS_S_TCPS].in_flags = SLAPD_POLL_FLAGS; + ct->fd[FDS_S_TCPS].out_flags = 0; + LDAPDebug( LDAP_DEBUG_HOUSE, + "listening for SSL connections on %d\n", socketdesc, 0, 0 ); + } else { + ct->fd[FDS_S_TCPS].fd = NULL; + } + +#if !defined(_WIN32) + /* The fds entry for the signalpipe is always FDS_SIGNAL_PIPE */ + ct->fd[FDS_SIGNAL_PIPE].fd = signalpipe[0]; + ct->fd[FDS_SIGNAL_PIPE].in_flags = SLAPD_POLL_FLAGS; + ct->fd[FDS_SIGNAL_PIPE].out_flags = 0; +#else + ct->fd[FDS_SIGNAL_PIPE].fd = NULL; +#endif + first_time_setup_pr_read_pds = 0; + } + + /* count is the number of entries we've place in the fds array. + * we always put n_tcps in slot FDS_N_TCPS, s_tcps in slot + * FDS_S_TCPS and the signal pipe in slot FDS_SIGNAL_PIPE + * so we now set count to 3 */ + count = 3; + + /* Walk down the list of active connections to find + * out which connections we should poll over. If a connection + * is no longer in use, we should remove it from the linked + * list. */ + c = connection_table_get_first_active_connection (ct); + while (c) + { + next = connection_table_get_next_active_connection (ct, c); + if ( c->c_mutex == NULL ) + { + connection_table_move_connection_out_of_active_list(ct,c); + } + else + { + PR_Lock( c->c_mutex ); + if (c->c_flags & CONN_FLAG_CLOSING) + { + /* A worker thread has marked that this connection + * should be closed by calling disconnect_server. + * move this connection out of the active list + * the last thread to use the connection will close it + */ + connection_table_move_connection_out_of_active_list(ct,c); + } + else if ( c->c_sd == SLAPD_INVALID_SOCKET ) + { + connection_table_move_connection_out_of_active_list(ct,c); + } + else if ( c->c_prfd != NULL) + { + if ((!c->c_gettingber) + && (c->c_threadnumber < max_threads_per_conn)) + { + ct->fd[count].fd = c->c_prfd; + ct->fd[count].in_flags = SLAPD_POLL_FLAGS; + /* slot i of the connection table is mapped to slot + * count of the fds array */ + c->c_fdi = count; + count++; + } + else + { + c->c_fdi = SLAPD_INVALID_SOCKET_INDEX; + } + } + PR_Unlock( c->c_mutex ); + } + c = next; + } + + if( num_to_read ) + (*num_to_read) = count; + +} + +#ifdef notdef /* GGOODREPL */ +static void +handle_timeout( void ) +{ + static time_t prevtime = 0; + static time_t housekeeping_fire_time = 0; + time_t curtime = current_time(); + + if (0 == prevtime) { + prevtime = time (&housekeeping_fire_time); + } + + if ( difftime(curtime, prevtime) >= + slapd_housekeeping_timer ) { + int num_active_threads; + + snmp_collator_update(); + + prevtime = curtime; + num_active_threads = active_threads; + if ( (num_active_threads == 0) || + (difftime(curtime, housekeeping_fire_time) >= + slapd_housekeeping_timer*3) ) { + housekeeping_fire_time = curtime; + housekeeping_start(curtime); + } + } + +} +#endif /* notdef */ + + +static int idletimeout_reslimit_handle = -1; + +/* + * Register the idletimeout with the binder-based resource limits + * subsystem. A SLAPI_RESLIMIT_STATUS_... code is returned. + */ +int +daemon_register_reslimits( void ) +{ + return( slapi_reslimit_register( SLAPI_RESLIMIT_TYPE_INT, "nsIdleTimeout", + &idletimeout_reslimit_handle )); +} + + +/* + * Compute the idle timeout for the connection. + * + * Note: this function must always be called with conn->c_mutex locked. + */ +static int +compute_idletimeout( slapdFrontendConfig_t *fecfg, Connection *conn ) +{ + int idletimeout; + + if ( slapi_reslimit_get_integer_limit( conn, idletimeout_reslimit_handle, + &idletimeout ) != SLAPI_RESLIMIT_STATUS_SUCCESS ) { + /* + * no limit associated with binder/connection or some other error + * occurred. use the default idle timeout. + */ + if ( conn->c_isroot ) { + idletimeout = 0; /* no limit for Directory Manager */ + } else { + idletimeout = fecfg->idletimeout; + } + } + + return( idletimeout ); +} + + +#ifdef _WIN32 +static void +handle_read_ready(Connection_Table *ct, fd_set *readfds) +{ + Connection *c= NULL; + time_t curtime = current_time(); + slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig(); + int idletimeout; + +#ifdef LDAP_DEBUG + if ( slapd_ldap_debug & LDAP_DEBUG_CONNS ) + { + connection_table_dump_activity_to_errors_log(ct); + } +#endif /* LDAP_DEBUG */ + + + /* Instead of going through the whole connection table to see which + * connections we can read from, we'll only check the slots in the + * linked list */ + c = connection_table_get_first_active_connection (ct); + while ( c!=NULL ) + { + if ( c->c_mutex != NULL ) + { + PR_Lock( c->c_mutex ); + if (connection_is_active_nolock (c) && c->c_gettingber == 0 ) + { + /* read activity */ + short readready= ( FD_ISSET( c->c_sd, readfds ) ); + + /* read activity */ + if ( readready ) + { + LDAPDebug( LDAP_DEBUG_CONNS, "read activity on %d\n", c->c_ci, 0, 0 ); + c->c_idlesince = curtime; + + /* This is where the work happens ! */ + connection_activity( c ); + + /* idle timeout */ + } + else if (( idletimeout = compute_idletimeout( + slapdFrontendConfig, c )) > 0 && + (curtime - c->c_idlesince) >= idletimeout && + NULL == c->c_ops ) + { + disconnect_server_nomutex( c, c->c_connid, -1, + SLAPD_DISCONNECT_IDLE_TIMEOUT, EAGAIN ); + } + } + PR_Unlock( c->c_mutex ); + } + c = connection_table_get_next_active_connection (ct, c); + } +} +#endif /* _WIN32 */ + + +static void +handle_pr_read_ready(Connection_Table *ct, PRIntn num_poll) +{ + Connection *c; + time_t curtime = current_time(); + slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig(); + int idletimeout; +#if defined( XP_WIN32 ) + int i; +#endif + +#if LDAP_DEBUG + if ( slapd_ldap_debug & LDAP_DEBUG_CONNS ) + { + connection_table_dump_activity_to_errors_log(ct); + } +#endif /* LDAP_DEBUG */ + +#if defined( XP_WIN32 ) + /* + * WIN32: this function is only called for SSL connections and + * num_poll indicates exactly how many PR fds we polled on. + */ + for ( i = 0; i < num_poll; i++ ) + { + short readready; + readready = (ct->fd[i].out_flags & SLAPD_POLL_FLAGS); + + /* Find the connection we are referring to */ + for ( c = connection_table_get_first_active_connection (ct); c != NULL; + c = connection_table_get_next_active_connection (ct, c) ) + { + if ( c->c_mutex != NULL ) + { + PR_Lock( c->c_mutex ); + if ( c->c_prfd == ct->fd[i].fd ) + { + break; /* c_mutex is still locked! */ + } + PR_Unlock( c->c_mutex ); + } + } + + if ( c == NULL ) + { /* connection not found! */ + LDAPDebug( LDAP_DEBUG_CONNS, "handle_pr_read_ready: " + "connection not found for poll slot %d\n", i,0,0 ); + } + else + { + /* c_mutex is still locked... check for activity and errors */ + if ( !readready && ct->fd[i].out_flags && c->c_prfd == ct->fd[i].fd ) + { + /* some error occured */ + LDAPDebug( LDAP_DEBUG_CONNS, + "poll says connection on sd %d is bad " + "(closing)\n", c->c_sd, 0, 0 ); + disconnect_server_nomutex( c, c->c_connid, -1, SLAPD_DISCONNECT_POLL, EPIPE ); + } + else if ( readready && c->c_prfd == ct->fd[i].fd ) + { + /* read activity */ + LDAPDebug( LDAP_DEBUG_CONNS, + "read activity on %d\n", i, 0, 0 ); + c->c_idlesince = curtime; + + /* This is where the work happens ! */ + connection_activity( c ); + } + else if (( idletimeout = compute_idletimeout( slapdFrontendConfig, + c )) > 0 && + c->c_prfd == ct->fd[i].fd && + (curtime - c->c_idlesince) >= idletimeout && + NULL == c->c_ops ) + { + /* idle timeout */ + disconnect_server_nomutex( c, c->c_connid, -1, + SLAPD_DISCONNECT_IDLE_TIMEOUT, EAGAIN ); + } + + PR_Unlock( c->c_mutex ); + } + } +#else + + /* + * non-WIN32: this function is called for all connections, so we + * traverse the entire active connection list to find any errors, + * activity, etc. + */ + for ( c = connection_table_get_first_active_connection (ct); c != NULL; + c = connection_table_get_next_active_connection (ct, c) ) + { + if ( c->c_mutex != NULL ) + { + PR_Lock( c->c_mutex ); + if ( connection_is_active_nolock (c) && c->c_gettingber == 0 ) + { + PRInt16 out_flags; + short readready; + + if (c->c_fdi != SLAPD_INVALID_SOCKET_INDEX) + { + out_flags = ct->fd[c->c_fdi].out_flags; + } + else + { + out_flags = 0; + } + + readready = ( out_flags & SLAPD_POLL_FLAGS ); + + if ( !readready && out_flags ) + { + /* some error occured */ + LDAPDebug( LDAP_DEBUG_CONNS, + "POLL_FN() says connection on sd %d is bad " + "(closing)\n", c->c_sd, 0, 0 ); + disconnect_server_nomutex( c, c->c_connid, -1, + SLAPD_DISCONNECT_POLL, EPIPE ); + } + else if ( readready ) + { + /* read activity */ + LDAPDebug( LDAP_DEBUG_CONNS, + "read activity on %d\n", c->c_ci, 0, 0 ); + c->c_idlesince = curtime; + + /* This is where the work happens ! */ + /* MAB: 25 jan 01, error handling added */ + if ((connection_activity( c )) == -1) { + /* This might happen as a result of + * trying to acquire a closing connection + */ + LDAPDebug (LDAP_DEBUG_ANY, + "connection_activity: abandoning conn %d as fd=%d is already closing\n", + c->c_connid,c->c_sd,0); + /* The call disconnect_server should do nothing, + * as the connection c should be already set to CLOSING */ + disconnect_server_nomutex( c, c->c_connid, -1, + SLAPD_DISCONNECT_POLL, EPIPE ); + } + } + else if (( idletimeout = compute_idletimeout( + slapdFrontendConfig, c )) > 0 && + (curtime - c->c_idlesince) >= idletimeout && + NULL == c->c_ops ) + { + /* idle timeout */ + disconnect_server_nomutex( c, c->c_connid, -1, + SLAPD_DISCONNECT_IDLE_TIMEOUT, EAGAIN ); + } + } + PR_Unlock( c->c_mutex ); + } + } +#endif +} + +/* + * wrapper functions required so we can implement ioblock_timeout and + * avoid blocking forever. + */ + +#define SLAPD_POLLIN 0 +#define SLAPD_POLLOUT 1 + +/* Return 1 if the given handle is ready for input or output, + * or if it becomes ready within g_ioblock_timeout [msec]. + * Return -1 if handle is not ready and g_ioblock_timeout > 0, + * or something goes seriously wrong. Otherwise, return 0. + * If -1 is returned, PR_GetError() explains why. + * Revision: handle changed to void * to allow 64bit support + */ +static int +slapd_poll( void *handle, int output, int secure ) +{ + int rc; + int ioblock_timeout = config_get_ioblocktimeout(); + +#if defined( XP_WIN32 ) + if( !secure ) { + fd_set handle_set; + struct timeval timeout; + int windows_handle = (int) handle; + + memset (&timeout, 0, sizeof(timeout)); + if (ioblock_timeout > 0) { + timeout.tv_sec = ioblock_timeout / 1000; + timeout.tv_usec = (ioblock_timeout % 1000) * 1000; + } + FD_ZERO(&handle_set); + FD_SET(windows_handle, &handle_set); + rc = output ? select(FD_SETSIZE, NULL, &handle_set, NULL, &timeout) + : select(FD_SETSIZE, &handle_set, NULL, NULL, &timeout); + } else { + struct POLL_STRUCT pr_pd; + PRIntervalTime timeout = PR_MillisecondsToInterval( ioblock_timeout ); + + if (timeout < 0) timeout = 0; + pr_pd.fd = (PRFileDesc *)handle; + pr_pd.in_flags = output ? PR_POLL_WRITE : PR_POLL_READ; + pr_pd.out_flags = 0; + rc = POLL_FN(&pr_pd, 1, timeout); + } +#else + struct POLL_STRUCT pr_pd; + PRIntervalTime timeout = PR_MillisecondsToInterval(ioblock_timeout); + + if (timeout < 0) timeout = 0; + pr_pd.fd = (PRFileDesc *)handle; + pr_pd.in_flags = output ? PR_POLL_WRITE : PR_POLL_READ; + pr_pd.out_flags = 0; + rc = POLL_FN(&pr_pd, 1, timeout); +#endif + + if (rc < 0) { +#if defined( XP_WIN32 ) + if( !secure ) { + int oserr = errno; + + LDAPDebug(LDAP_DEBUG_CONNS, "slapd_poll(%d) error %d (%s)\n", + handle, oserr, slapd_system_strerror(oserr)); + if ( SLAPD_SYSTEM_WOULD_BLOCK_ERROR(oserr)) { + rc = 0; /* try again */ + } + } else { + PRErrorCode prerr = PR_GetError(); + LDAPDebug(LDAP_DEBUG_CONNS, "slapd_poll(%d) " + SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n", + handle, prerr, slapd_pr_strerror(prerr)); + if ( prerr == PR_PENDING_INTERRUPT_ERROR || + SLAPD_PR_WOULD_BLOCK_ERROR(prerr)) { + rc = 0; /* try again */ + } + } +#else + PRErrorCode prerr = PR_GetError(); + LDAPDebug(LDAP_DEBUG_ANY, "slapd_poll(%d) " + SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n", + handle, prerr, slapd_pr_strerror(prerr)); + if ( prerr == PR_PENDING_INTERRUPT_ERROR || + SLAPD_PR_WOULD_BLOCK_ERROR(prerr)) { + rc = 0; /* try again */ + } +#endif + + } else if (rc == 0 && ioblock_timeout > 0) { + PRIntn ihandle; +#if !defined( XP_WIN32 ) + ihandle = PR_FileDesc2NativeHandle((PRFileDesc *)handle); +#else + if( secure ) + ihandle = PR_FileDesc2NativeHandle((PRFileDesc *)handle); + else + ihandle = (PRIntn)handle; +#endif + LDAPDebug(LDAP_DEBUG_ANY, "slapd_poll(%d) timed out\n", + ihandle, 0, 0); +#if defined( XP_WIN32 ) + /* + * Bug 624303 - This connection will be cleaned up soon. + * During cleanup (see connection_cleanup()), SSL3_SendAlert() + * will be called by PR_Close(), and its default wTimeout + * in sslSocket associated with the handle + * is no time out (I gave up after waited for 30 minutes). + * It was during this closing period that server won't + * response to new connection requests. + * PR_Send() null is a hack here to change the default wTimeout + * (see ssl_Send()) to one second which affects PR_Close() + * only in the current scenario. + */ + if( secure ) { + PR_Send ((PRFileDesc *)handle, NULL, 0, 0, PR_SecondsToInterval(1)); + } +#endif + PR_SetError(PR_IO_TIMEOUT_ERROR, EAGAIN); /* timeout */ + rc = -1; + } + return rc; +} + +/* The following 4 functions each read or write count bytes from or to + * a socket handle. If all goes well, they return the same count; + * otherwise they return -1 and PR_GetError() explains the problem. + * Revision: handle changed to struct lextiof_socket_private * and first + * argument which used to be handle is now ignored. + */ +int +secure_read_function( int ignore, void *buffer, int count, struct lextiof_socket_private *handle ) +{ + int gotbytes = 0; + int bytes; + int ioblock_timeout = config_get_ioblocktimeout(); + PRIntervalTime pr_timeout = PR_MillisecondsToInterval(ioblock_timeout); + + if (handle == SLAPD_INVALID_SOCKET) { + PR_SetError(PR_NOT_SOCKET_ERROR, EBADF); + } else { + while (1) { + bytes = PR_Recv( (PRFileDesc *)handle, (char *)buffer + gotbytes, + count - gotbytes, 0, pr_timeout ); + if (bytes > 0) { + gotbytes += bytes; + } else if (bytes < 0) { + PRErrorCode prerr = PR_GetError(); + +#ifdef _WIN32 + /* we need to do this because on NT, once an I/O + times out on an NSPR socket, that socket must + be closed before any other I/O can happen in + this thread. + */ + if (prerr == PR_IO_TIMEOUT_ERROR){ + Connection *conn = connection_table_get_connection_from_fd(the_connection_table,(PRFileDesc *)handle); + if (conn == NULL) + return -1; + + disconnect_server (conn, conn->c_connid, -1, SLAPD_DISCONNECT_NTSSL_TIMEOUT, 0); + /* Disconnect_server just tells the poll thread that the + * socket should be closed. We'll sleep 2 seconds here to + * to make sure that the poll thread has time to run + * and close this socket. */ + DS_Sleep(PR_SecondsToInterval(2)); + + LDAPDebug(LDAP_DEBUG_CONNS, "SSL PR_Recv(%d) " + SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n", + handle, prerr, slapd_pr_strerror(prerr)); + + return -1; + } +#endif + + LDAPDebug(LDAP_DEBUG_CONNS, + "SSL PR_Recv(%d) error %d (%s)\n", + handle, prerr, slapd_pr_strerror(prerr)); + if ( !SLAPD_PR_WOULD_BLOCK_ERROR(prerr) ) { + break; /* fatal error */ + } + } else if (gotbytes < count) { + LDAPDebug(LDAP_DEBUG_CONNS, + "SSL PR_Recv(%d) 0 (EOF)\n", /* disconnected */ + handle, 0, 0); + PR_SetError(PR_PIPE_ERROR, EPIPE); + break; + } + if (gotbytes == count) { /* success */ + return count; + } else if (gotbytes > count) { /* too many bytes */ + PR_SetError(PR_BUFFER_OVERFLOW_ERROR, EMSGSIZE); + break; + } else if (slapd_poll(handle, SLAPD_POLLIN, 1) < 0) { /* error */ + break; + } + } + } + return -1; +} + + +/* + * Revision: handle changed to struct lextiof_socket_private * and first + * argument which used to be handle is now ignored. + */ +int +secure_write_function( int ignore, const void *buffer, int count, struct lextiof_socket_private *handle ) +{ + int sentbytes = 0; + int bytes; + + if (handle == SLAPD_INVALID_SOCKET) { + PR_SetError(PR_NOT_SOCKET_ERROR, EBADF); + } else { + while (1) { + if (slapd_poll(handle, SLAPD_POLLOUT, 1) < 0) { /* error */ + break; + } + bytes = PR_Write((PRFileDesc *)handle, (char *)buffer + sentbytes, + count - sentbytes); + if (bytes > 0) { + sentbytes += bytes; + } else if (bytes < 0) { + PRErrorCode prerr = PR_GetError(); + LDAPDebug(LDAP_DEBUG_CONNS, "SSL PR_Write(%d) " + SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n", + handle, prerr, slapd_pr_strerror( prerr )); + if ( !SLAPD_PR_WOULD_BLOCK_ERROR(prerr)) { + break; /* fatal error */ + } + } else if (sentbytes < count) { + LDAPDebug( LDAP_DEBUG_CONNS, + "SSL PR_Write(%d) 0\n", /* ??? */ handle, 0, 0); + PR_SetError(PR_PIPE_ERROR, EPIPE); + break; + } + if (sentbytes == count) { /* success */ + return count; + } else if (sentbytes > count) { /* too many bytes */ + PR_SetError(PR_BUFFER_OVERFLOW_ERROR, EMSGSIZE); + break; + } + } + } + return -1; +} + +/* stub functions required because we need to call send/recv on NT, + * but the SDK requires functions with a read/write signature. + * Revision: handle changed to struct lextiof_socket_private * and first + * argument which used to be handle is now ignored. + */ +int +read_function(int ignore, void *buffer, int count, struct lextiof_socket_private *handle ) +{ + int gotbytes = 0; + int bytes; +#if !defined( XP_WIN32 ) + PRIntervalTime pr_timeout = PR_MillisecondsToInterval(1000); +#endif + + if (handle == SLAPD_INVALID_SOCKET) { + PR_SetError(PR_NOT_SOCKET_ERROR, EBADF); + } else { + while (1) { +#if !defined( XP_WIN32 ) + bytes = PR_Recv((PRFileDesc *)handle, (char *)buffer + gotbytes, + count - gotbytes, 0, pr_timeout); +#else + bytes = recv((int)handle, (char *)buffer + gotbytes, + count - gotbytes, 0); +#endif + if (bytes > 0) { + gotbytes += bytes; + } else if (bytes < 0) { +#if !defined( XP_WIN32 ) + PRErrorCode prerr = PR_GetError(); + + LDAPDebug(LDAP_DEBUG_CONNS, "PR_Recv(%d) " + SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n", + handle, prerr, slapd_pr_strerror( prerr )); + if ( !SLAPD_PR_WOULD_BLOCK_ERROR(prerr)) { +#else + int oserr = errno; + + LDAPDebug(LDAP_DEBUG_CONNS, "recv(%d) OS error %d (%s)\n", + handle, oserr, slapd_system_strerror(oserr)); + if ( !SLAPD_SYSTEM_WOULD_BLOCK_ERROR(oserr)) { + PR_SetError(PR_UNKNOWN_ERROR, oserr); +#endif + break; /* fatal error */ + } + } else if (gotbytes < count) { /* disconnected */ +#if !defined( XP_WIN32 ) + LDAPDebug(LDAP_DEBUG_CONNS, "PR_Recv(%d) 0 (EOF)\n", + handle, 0, 0); +#else + LDAPDebug(LDAP_DEBUG_CONNS, "recv(%d) 0 (EOF)\n", + handle, 0, 0); +#endif + PR_SetError(PR_PIPE_ERROR, EPIPE); + break; + } + if (gotbytes == count) { /* success */ + return count; + } else if (gotbytes > count) { /* too many bytes */ + PR_SetError(PR_BUFFER_OVERFLOW_ERROR, EMSGSIZE); + break; + } + /* we did not get the whole PDU + * call slapd_poll before starting a new read to get + * sure some new data have been received and + * thus avoid active looping in the while + */ + if (slapd_poll(handle, SLAPD_POLLIN, 0) < 0) { + break; + } + } + } + return -1; +} + +/* +Slapd's old (3.x) network I/O code works something like this: + when I want to send some data to the client, I will call send(). + That might block for a long time, resulting in thread pool starvation, + so let's not call it unless we're sure that data can be buffered + locally. The mechanism for achieving this is to call select() + (poll() on UNIX), on the target socket, passing a short timeout + (configurable via cn=config). + + Now, this means that to send some data we're making two system + calls. Slowness results. + + I did some research and found the following in the MSDN + that NT4.0 and beyond do support the configuration of a send timeout + on sockets, so this is code which makes use of that and saves the + call to select. +*/ + +/*Revision: handle changed from int to void * to allow 64bit support + * + */ +static int send_with_timeout(void *handle, const char * buffer, int count,int *bytes_sent) +{ + int ret = 0; +#if defined( XP_WIN32 ) + *bytes_sent = send((SOCKET)handle, buffer,count,0); +#else + *bytes_sent = PR_Write((PRFileDesc *)handle,buffer,count); + if (*bytes_sent < 0) + { + PRErrorCode prerr = PR_GetError(); + if (SLAPD_PR_WOULD_BLOCK_ERROR(prerr)) + { + if ((ret = slapd_poll(handle, SLAPD_POLLOUT, 0)) < 0) + { /* error */ + *bytes_sent = 0; + return ret; + } + + } + } +#endif + + return ret; +} + +int +write_function(int ignore, const void *buffer, int count, struct lextiof_socket_private *handle) +{ + int sentbytes = 0; + int bytes; + + + if (handle == SLAPD_INVALID_SOCKET) { + PR_SetError(PR_NOT_SOCKET_ERROR, EBADF); + } else { + while (1) { + + if (send_with_timeout(handle, (char *)buffer + sentbytes, count - sentbytes,&bytes) < 0) { /* error */ + break; + } + if (bytes > 0) { + sentbytes += bytes; + } else if (bytes < 0) { +#if !defined( XP_WIN32 ) + PRErrorCode prerr = PR_GetError(); + + LDAPDebug(LDAP_DEBUG_CONNS, "PR_Write(%d) " + SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n", + handle, prerr, slapd_pr_strerror(prerr)); + if ( !SLAPD_PR_WOULD_BLOCK_ERROR(prerr)) { +#else + int oserr = errno; /* DBDB this is almost certainly wrong, should be a call to WSAGetLastError() */ + + LDAPDebug(LDAP_DEBUG_CONNS, "send(%d) error %d (%s)\n", + handle, oserr, slapd_system_strerror(oserr)); + if ( !SLAPD_SYSTEM_WOULD_BLOCK_ERROR(oserr)) { + PR_SetError(PR_UNKNOWN_ERROR, oserr); +#endif + break; /* fatal error */ + } + } else if (sentbytes < count) { + LDAPDebug(LDAP_DEBUG_CONNS, "send(%d) 0\n", /* ??? */ + handle, 0, 0); + PR_SetError(PR_PIPE_ERROR, EPIPE); + break; + } + if (sentbytes == count) { /* success */ + return count; + } else if (sentbytes > count) { /* too many bytes */ + PR_SetError(PR_BUFFER_OVERFLOW_ERROR, EMSGSIZE); + break; + } + } + } + return -1; +} + +int connection_type = -1; /* The type number assigned by the Factory for 'Connection' */ + +void +daemon_register_connection() +{ + if(connection_type==-1) + { + /* The factory is given the name of the object type, in + * return for a type handle. Whenever the object is created + * or destroyed the factory is called with the handle so + * that it may call the constructors or destructors registered + * with it. + */ + connection_type= factory_register_type(SLAPI_EXT_CONNECTION,offsetof(Connection,c_extension)); + } +} + +/* NOTE: this routine is not reentrant */ +static int +handle_new_connection(Connection_Table *ct, int tcps, PRFileDesc *pr_acceptfd, int secure) +{ + int ns = 0; + Connection *conn = NULL; + /* struct sockaddr_in from;*/ + PRNetAddr from; + PRFileDesc *pr_clonefd = NULL; + + if ( (ns = accept_and_configure( tcps, pr_acceptfd, &from, + sizeof(from), secure, &pr_clonefd)) == SLAPD_INVALID_SOCKET ) { + return -1; + } + + /* get a new Connection from the Connection Table */ + conn= connection_table_get_connection(ct,ns); + if(conn==NULL) + { + PR_Close(pr_acceptfd); + return -1; + } + PR_Lock( conn->c_mutex ); + +#if !defined( XP_WIN32 ) + ber_sockbuf_set_option(conn->c_sb,LBER_SOCKBUF_OPT_DESC,&pr_clonefd); +#else + if( !secure ) + ber_sockbuf_set_option(conn->c_sb,LBER_SOCKBUF_OPT_DESC,&ns); + else + ber_sockbuf_set_option(conn->c_sb,LBER_SOCKBUF_OPT_DESC,&pr_clonefd); +#endif + + conn->c_sd = ns; + conn->c_prfd = pr_clonefd; + conn->c_flags &= ~CONN_FLAG_CLOSING; + + /* Store the fact that this new connection is an SSL connection */ + if (secure) { + conn->c_flags |= CONN_FLAG_SSL; + } + +#ifndef _WIN32 + /* + * clear the "returned events" field in ns' slot within the poll fds + * array so that handle_read_ready() doesn't look at out_flags for an + * old connection by mistake and do something bad such as close the + * connection we just accepted. + */ + + /* Dont have to worry about this now because of our mapping from + * the connection table to the fds array. This new connection + * won't have a mapping. */ + /* fds[ns].out_flags = 0; */ +#endif + + if (secure) { + /*structure added to enable 64bit support changed from + *the commented code that follows each of the next two + *blocks of code + */ + struct lber_x_ext_io_fns *func_pointers = malloc(LBER_X_EXTIO_FNS_SIZE); + func_pointers->lbextiofn_size = LBER_X_EXTIO_FNS_SIZE; + func_pointers->lbextiofn_read = secure_read_function; + func_pointers->lbextiofn_write = secure_write_function; + func_pointers->lbextiofn_writev = NULL; + func_pointers->lbextiofn_socket_arg = (struct lextiof_socket_private *) pr_clonefd; + ber_sockbuf_set_option( conn->c_sb, + LBER_SOCKBUF_OPT_EXT_IO_FNS, func_pointers); + + /* changed here by Cheston + ber_sockbuf_set_option( conn->c_sb, + LBER_SOCKBUF_OPT_READ_FN, (void *)secure_read_function ); + ber_sockbuf_set_option( conn->c_sb, + LBER_SOCKBUF_OPT_WRITE_FN, (void *)secure_write_function ); + */ + } else { + struct lber_x_ext_io_fns *func_pointers = malloc(LBER_X_EXTIO_FNS_SIZE); + func_pointers->lbextiofn_size = LBER_X_EXTIO_FNS_SIZE; + func_pointers->lbextiofn_read = read_function; + func_pointers->lbextiofn_write = write_function; + func_pointers->lbextiofn_writev = NULL; +#ifdef _WIN32 + func_pointers->lbextiofn_socket_arg = (struct lextiof_socket_private *) ns; +#else + func_pointers->lbextiofn_socket_arg = (struct lextiof_socket_private *) pr_clonefd; +#endif + ber_sockbuf_set_option( conn->c_sb, + LBER_SOCKBUF_OPT_EXT_IO_FNS, func_pointers); + /* + ber_sockbuf_set_option( conn->c_sb, + LBER_SOCKBUF_OPT_READ_FN, (void *)read_function ); + ber_sockbuf_set_option( conn->c_sb, + LBER_SOCKBUF_OPT_WRITE_FN, (void *)write_function ); + */ + } + +#if defined(NET_SSL) + if( secure && config_get_SSLclientAuth() != SLAPD_SSLCLIENTAUTH_OFF ) { + /* Prepare to handle the client's certificate (if any): */ + int rv; + + rv = slapd_ssl_handshakeCallback (conn->c_prfd, (void*)handle_handshake_done, conn); + + if (rv < 0) { + PRErrorCode prerr = PR_GetError(); + LDAPDebug (LDAP_DEBUG_ANY, "SSL_HandshakeCallback() %d " + SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n", + rv, prerr, slapd_pr_strerror( prerr )); + } + rv = slapd_ssl_badCertHook (conn->c_prfd, (void*)handle_bad_certificate, conn); + + if (rv < 0) { + PRErrorCode prerr = PR_GetError(); + LDAPDebug (LDAP_DEBUG_ANY, "SSL_BadCertHook(%i) %i " + SLAPI_COMPONENT_NAME_NSPR " error %d\n", + conn->c_sd, rv, prerr); + } + } +#endif + + connection_reset(conn, ns, &from, sizeof(from), secure); + + /* Call the plugin extension constructors */ + conn->c_extension = factory_create_extension(connection_type,conn,NULL /* Parent */); + + + /* Add this connection slot to the doubly linked list of active connections. This + * list is used to find the connections that should be used in the poll call. This + * connection will be added directly after slot 0 which serves as the head of the list */ + if ( conn != NULL && conn->c_next == NULL && conn->c_prev == NULL ) + { + /* Now give the new connection to the connection code */ + connection_table_move_connection_on_to_active_list(the_connection_table,conn); + } + + PR_Unlock( conn->c_mutex ); + + connection_new_private(conn); + + g_increment_current_conn_count(); + + return 0; +} + +static int init_shutdown_detect() +{ + +#ifdef _WIN32 + PRThread *service_exit_wait_tid; +#else + /* First of all, we must reset the signal mask to get rid of any blockages + * the process may have inherited from its parent (such as the console), which + * might result in the process not delivering those blocked signals, and thus, + * misbehaving.... + */ + { + int rc; + sigset_t proc_mask; + + LDAPDebug( LDAP_DEBUG_TRACE, "Reseting signal mask....\n", 0, 0, 0); + (void)sigemptyset( &proc_mask ); + rc = pthread_sigmask( SIG_SETMASK, &proc_mask, NULL ); + LDAPDebug( LDAP_DEBUG_TRACE, " %s \n", + rc ? "Failed to reset signal mask":"....Done (signal mask reset)!!", 0, 0 ); + } +#endif + +#ifdef _WIN32 + + /* Create a thread to wait on the Win32 event which will + be signalled by the watchdog when the Service is + being halted. */ + service_exit_wait_tid = PR_CreateThread( PR_USER_THREAD, + (VFP) (void *) slapd_service_exit_wait, (void *) NULL, + PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_UNJOINABLE_THREAD, + SLAPD_DEFAULT_THREAD_STACKSIZE); + if( service_exit_wait_tid == NULL ) { + LDAPDebug( LDAP_DEBUG_ANY, + "Error: PR_CreateThread(slapd_service_exit_wait) failed\n", 0, 0, 0 ); + } +#elif defined ( HPUX10 ) + PR_CreateThread ( PR_USER_THREAD, + catch_signals, + NULL, + PR_PRIORITY_NORMAL, + PR_GLOBAL_THREAD, + PR_UNJOINABLE_THREAD, + SLAPD_DEFAULT_THREAD_STACKSIZE); +#else +#ifdef HPUX11 + /* In the optimized builds for HPUX, the signal handler doesn't seem + * to get set correctly unless the primordial thread gets a chance + * to run before we make the call to SIGNAL. (At this point the + * the primordial thread has spawned the daemon thread which called + * this function.) The call to DS_Sleep will give the primordial + * thread a chance to run. + */ + DS_Sleep(0); +#endif + (void) SIGNAL( SIGPIPE, SIG_IGN ); + (void) SIGNAL( SIGCHLD, slapd_wait4child ); +#ifndef LINUX + /* linux uses USR1/USR2 for thread synchronization, so we aren't + * allowed to mess with those. + */ + (void) SIGNAL( SIGUSR1, slapd_do_nothing ); + (void) SIGNAL( SIGUSR2, set_shutdown ); +#endif + (void) SIGNAL( SIGTERM, set_shutdown ); + (void) SIGNAL( SIGHUP, set_shutdown ); +#endif /* _WIN32 */ + return 0; +} + +#if defined( XP_WIN32 ) +static void +unfurl_banners(Connection_Table *ct,daemon_ports_t *ports, int n_tcps, PRFileDesc *s_tcps) +#else +static void +unfurl_banners(Connection_Table *ct,daemon_ports_t *ports, PRFileDesc *n_tcps, PRFileDesc *s_tcps) +#endif +{ + slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig(); + char addrbuf[ 256 ]; + + if ( ct->size <= slapdFrontendConfig->reservedescriptors ) { +#ifdef _WIN32 + LDAPDebug( LDAP_DEBUG_ANY, + "ERROR: Not enough descriptors to accept any connections. " + "This may be because the maxdescriptors configuration " + "directive is too small, or the reservedescriptors " + "configuration directive is too large. " + "Try increasing the number of descriptors available to " + "the slapd process. The current value is %d. %d " + "descriptors are currently reserved for internal " + "slapd use, so the total number of descriptors available " + "to the process must be greater than %d.\n", + ct->size, slapdFrontendConfig->reservedescriptors, slapdFrontendConfig->reservedescriptors ); +#else /* _WIN32 */ + LDAPDebug( LDAP_DEBUG_ANY, + "ERROR: Not enough descriptors to accept any connections. " + "This may be because the maxdescriptors configuration " + "directive is too small, the hard limit on descriptors is " + "too small (see limit(1)), or the reservedescriptors " + "configuration directive is too large. " + "Try increasing the number of descriptors available to " + "the slapd process. The current value is %d. %d " + "descriptors are currently reserved for internal " + "slapd use, so the total number of descriptors available " + "to the process must be greater than %d.\n", + ct->size, slapdFrontendConfig->reservedescriptors, slapdFrontendConfig->reservedescriptors ); +#endif /* _WIN32 */ + exit( 1 ); + } + + /* + * This final startup message gives a definite signal to the admin + * program that the server is up. It must contain the string + * "slapd started." because some of the administrative programs + * depend on this. See ldap/admin/lib/dsalib_updown.c. + */ +#if !defined( XP_WIN32 ) + if ( n_tcps != NULL ) { /* standard LDAP */ +#else + if ( n_tcps != SLAPD_INVALID_SOCKET ) { /* standard LDAP */ +#endif + + LDAPDebug( LDAP_DEBUG_ANY, + "slapd started. Listening on %s port %d for LDAP requests\n", + netaddr2string(&ports->n_listenaddr, addrbuf, sizeof(addrbuf)), + ports->n_port, 0 ); + } + + if ( s_tcps != NULL ) { /* LDAP over SSL; separate port */ + LDAPDebug( LDAP_DEBUG_ANY, + "Listening on %s port %d for LDAPS requests\n", + netaddr2string(&ports->s_listenaddr, addrbuf, sizeof(addrbuf)), + ports->s_port, 0 ); + } +} + +#if defined( _WIN32 ) +/* On Windows, we signal the SCM when we're ready to accept connections */ +static int +write_pid_file() +{ + if( SlapdIsAService() ) + { + /* Initialization complete and successful. Set service to running */ + LDAPServerStatus.dwCurrentState = SERVICE_RUNNING; + LDAPServerStatus.dwCheckPoint = 0; + LDAPServerStatus.dwWaitHint = 0; + + if (!SetServiceStatus(hLDAPServerServiceStatus, &LDAPServerStatus)) { + ReportSlapdEvent(EVENTLOG_INFORMATION_TYPE, MSG_SERVER_START_FAILED, 1, + "Could not set Service status."); + exit(1); + } + } + + ReportSlapdEvent(EVENTLOG_INFORMATION_TYPE, MSG_SERVER_STARTED, 0, NULL ); + return 0; +} +#else /* WIN32 */ +/* On UNIX, we create a file with our PID in it */ +static int +write_pid_file() +{ + FILE *fp = NULL; + /* + * The following section of code is closely coupled with the + * admin programs. Please do not make changes here without + * consulting the start/stop code for the admin code. + */ + if ( (fp = fopen( get_pid_file(), "w" )) != NULL ) { + fprintf( fp, "%d\n", getpid() ); + fclose( fp ); + return 0; + } else + { + return -1; + } +} +#endif /* WIN32 */ + +static void +set_shutdown (int sig) +{ + /* don't log anything from a signal handler: + * you could be holding a lock when the signal was trapped. more + * specifically, you could be holding the logfile lock (and deadlock + * yourself). + */ +#if 0 + LDAPDebug( LDAP_DEBUG_ANY, "slapd got shutdown signal\n", 0, 0, 0 ); +#endif + g_set_shutdown( SLAPI_SHUTDOWN_SIGNAL ); +#ifndef _WIN32 +#ifndef LINUX + /* don't mess with USR1/USR2 on linux, used by libpthread */ + (void) SIGNAL( SIGUSR2, set_shutdown ); +#endif + (void) SIGNAL( SIGTERM, set_shutdown ); + (void) SIGNAL( SIGHUP, set_shutdown ); +#endif +} + +#ifndef LINUX +void +slapd_do_nothing (int sig) +{ + /* don't log anything from a signal handler: + * you could be holding a lock when the signal was trapped. more + * specifically, you could be holding the logfile lock (and deadlock + * yourself). + */ +#if 0 + LDAPDebug( LDAP_DEBUG_TRACE, "slapd got SIGUSR1\n", 0, 0, 0 ); +#endif +#ifndef _WIN32 + (void) SIGNAL( SIGUSR1, slapd_do_nothing ); +#endif + +#if 0 + /* + * Actually do a little more: dump the conn struct and + * send it to a tmp file + */ + connection_table_dump(connection_table); +#endif +} +#endif /* LINUX */ + +#ifndef _WIN32 +void +slapd_wait4child(int sig) +{ + WAITSTATUSTYPE status; + + /* don't log anything from a signal handler: + * you could be holding a lock when the signal was trapped. more + * specifically, you could be holding the logfile lock (and deadlock + * yourself). + */ +#if 0 + LDAPDebug( LDAP_DEBUG_ARGS, "listener: catching SIGCHLD\n", 0, 0, 0 ); +#endif +#ifdef USE_WAITPID + while (waitpid ((pid_t) -1, 0, WAIT_FLAGS) > 0) +#else /* USE_WAITPID */ + while ( wait3( &status, WAIT_FLAGS, 0 ) > 0 ) +#endif /* USE_WAITPID */ + ; /* NULL */ + + (void) SIGNAL( SIGCHLD, slapd_wait4child ); +} +#endif + +#ifdef XP_WIN32 +static int +createlistensocket(unsigned short port, const PRNetAddr *listenaddr) +{ + int tcps; + struct sockaddr_in addr; + char *logname = "createlistensocket"; + char addrbuf[ 256 ]; + + if (!port) goto suppressed; + + PR_ASSERT( listenaddr != NULL ); + + /* create TCP socket */ + if ((tcps = socket(AF_INET, SOCK_STREAM, 0)) + == SLAPD_INVALID_SOCKET) { + int oserr = errno; + + slapi_log_error(SLAPI_LOG_FATAL, logname, + "socket() failed: OS error %d (%s)\n", + oserr, slapd_system_strerror( oserr )); + goto failed; + } + + /* initialize listener address */ + (void) memset( (void *) &addr, '\0', sizeof(addr) ); + addr.sin_family = AF_INET; + addr.sin_port = htons( port ); + if (listenaddr->raw.family == PR_AF_INET) { + addr.sin_addr.s_addr = listenaddr->inet.ip; + } else if (PR_IsNetAddrType(listenaddr,PR_IpAddrAny)) { + addr.sin_addr.s_addr = INADDR_ANY; + } else { + if (!PR_IsNetAddrType(listenaddr,PR_IpAddrV4Mapped)) { + /* + * When Win32 supports IPv6, we will be able to use IPv6 + * addresses here. But not yet. + */ + slapi_log_error(SLAPI_LOG_FATAL, logname, + "unable to listen on %s port %d (IPv6 addresses " + "are not supported on this platform)\n", + netaddr2string(listenaddr, addrbuf, sizeof(addrbuf)), + port ); + goto failed; + } + + addr.sin_addr.s_addr = listenaddr->ipv6.ip.pr_s6_addr32[3]; + } + + LDAPDebug( LDAP_DEBUG_TRACE, "%s - binding to %s:%d\n", + logname, inet_ntoa( addr.sin_addr ), port ) + + if ( bind( tcps, (struct sockaddr *) &addr, sizeof(addr) ) == -1 ) { + int oserr = errno; + + slapi_log_error(SLAPI_LOG_FATAL, logname, + "bind() on %s port %d failed: OS error %d (%s)\n", + inet_ntoa( addr.sin_addr ), port, oserr, + slapd_system_strerror( oserr )); + goto failed; + } + + return tcps; + +failed: + WSACleanup(); + exit( 1 ); +suppressed: + return -1; +} /* createlistensocket */ +#endif /* XP_WIN32 */ + + +static PRFileDesc * +createprlistensocket(unsigned short port, const PRNetAddr *listenaddr, + int secure) +{ + PRFileDesc *sock; + PRNetAddr sa_server; + PRErrorCode prerr = 0; + PRSocketOptionData pr_socketoption; + char addrbuf[ 256 ]; + char *logname = "createprlistensocket"; + + if (!port) goto suppressed; + + PR_ASSERT( listenaddr != NULL ); + + /* create TCP socket */ + if ((sock = PR_OpenTCPSocket(PR_AF_INET6)) == SLAPD_INVALID_SOCKET) { + prerr = PR_GetError(); + slapi_log_error(SLAPI_LOG_FATAL, logname, + "PR_OpenTCPSocket(PR_AF_INET6) failed: %s error %d (%s)\n", + SLAPI_COMPONENT_NAME_NSPR, prerr, slapd_pr_strerror(prerr)); + goto failed; + } + + pr_socketoption.option = PR_SockOpt_Reuseaddr; + pr_socketoption.value.reuse_addr = 1; + if ( PR_SetSocketOption(sock, &pr_socketoption ) == PR_FAILURE) { + prerr = PR_GetError(); + slapi_log_error(SLAPI_LOG_FATAL, logname, + "PR_SetSocketOption(PR_SockOpt_Reuseaddr) failed: %s error %d (%s)\n", + SLAPI_COMPONENT_NAME_NSPR, prerr, slapd_pr_strerror( prerr )); + goto failed; + } + + /* set up listener address, including port */ + memcpy(&sa_server, listenaddr, sizeof(sa_server)); + if ( PR_SetNetAddr(PR_IpAddrNull, PR_AF_INET6, port, &sa_server) + != PR_SUCCESS ) { + prerr = PR_GetError(); + slapi_log_error(SLAPI_LOG_FATAL, logname, + "PR_SetNetAddr() failed: %s error %d (%s)\n", + SLAPI_COMPONENT_NAME_NSPR, + prerr, slapd_pr_strerror(prerr)); + goto failed; + } + + if ( PR_Bind(sock, &sa_server) == PR_FAILURE) { + prerr = PR_GetError(); + slapi_log_error(SLAPI_LOG_FATAL, logname, + "PR_Bind() on %s port %d failed: %s error %d (%s)\n", + netaddr2string(&sa_server, addrbuf, sizeof(addrbuf)), port, + SLAPI_COMPONENT_NAME_NSPR, prerr, slapd_pr_strerror(prerr)); + goto failed; + } + + return( sock ); + +failed: +#ifdef XP_WIN32 + WSACleanup(); +#endif /* XP_WIN32 */ + exit( 1 ); + +suppressed: + return (PRFileDesc *)-1; +} /* createprlistensocket */ + + +/* + * Initialize the *addr structure based on listenhost. + * Returns: 0 if successful and -1 if not (after logging an error message). + */ +int +slapd_listenhost2addr(const char *listenhost, PRNetAddr *addr) +{ + char *logname = "slapd_listenhost2addr"; + PRErrorCode prerr = 0; + PRHostEnt hent; + char hbuf[ PR_NETDB_BUF_SIZE ]; + + PR_ASSERT( addr != NULL ); + + if (NULL == listenhost) { + /* listen on all interfaces */ + if ( PR_SUCCESS != PR_SetNetAddr(PR_IpAddrAny, PR_AF_INET6, 0, addr)) { + prerr = PR_GetError(); + slapi_log_error( SLAPI_LOG_FATAL, logname, + "PR_SetNetAddr(PR_IpAddrAny) failed - %s error %d (%s)\n", + SLAPI_COMPONENT_NAME_NSPR, prerr, slapd_pr_strerror(prerr)); + goto failed; + } + } else if (PR_SUCCESS == PR_StringToNetAddr(listenhost, addr)) { + if (PR_AF_INET == PR_NetAddrFamily(addr)) { + PRUint32 ipv4ip = addr->inet.ip; + memset(addr, 0, sizeof(PRNetAddr)); + PR_ConvertIPv4AddrToIPv6(ipv4ip, &addr->ipv6.ip); + addr->ipv6.family = PR_AF_INET6; + } + } else if (PR_SUCCESS == PR_GetIPNodeByName(listenhost, + PR_AF_INET6, PR_AI_DEFAULT | PR_AI_ALL, + hbuf, sizeof(hbuf), &hent )) { + /* just use the first IP address returned */ + if (PR_EnumerateHostEnt(0, &hent, 0, addr) < 0) { + slapi_log_error( SLAPI_LOG_FATAL, logname, + "PR_EnumerateHostEnt() failed - %s error %d (%s)\n", + SLAPI_COMPONENT_NAME_NSPR, prerr, slapd_pr_strerror(prerr)); + goto failed; + } + } else { /* failure */ + slapi_log_error( SLAPI_LOG_FATAL, logname, + "PR_GetIPNodeByName(%s) failed - %s error %d (%s)\n", + listenhost, SLAPI_COMPONENT_NAME_NSPR, prerr, + slapd_pr_strerror(prerr)); + goto failed; + } + + return( 0 ); + +failed: + return( -1 ); +} + + +/* + * Map addr to a string equivalent and place the result in addrbuf. + */ +static const char * +netaddr2string(const PRNetAddr *addr, char *addrbuf, size_t addrbuflen) +{ + const char *retstr; + + if (NULL == addr || PR_IsNetAddrType(addr, PR_IpAddrAny)) { + retstr = "All Interfaces"; + } else if (PR_IsNetAddrType(addr, PR_IpAddrLoopback)) { + if ( addr->raw.family == PR_AF_INET6 && + !PR_IsNetAddrType(addr, PR_IpAddrV4Mapped)) { + retstr = "IPv6 Loopback"; + } else { + retstr = "Loopback"; + } + } else if (PR_SUCCESS == PR_NetAddrToString( addr, addrbuf, addrbuflen)) { + if (0 == strncmp( addrbuf, "::ffff:", 7 )) { + /* IPv4 address mapped into IPv6 address space */ + retstr = addrbuf + 7; + } else { + /* full blown IPv6 address */ + retstr = addrbuf; + } + } else { /* punt */ + retstr = "address conversion failed"; + } + + return(retstr); +} + + +static int +createsignalpipe( void ) +{ +#if defined( _WIN32 ) + if ( PR_NewTCPSocketPair(&signalpipe[0])) { + PRErrorCode prerr = PR_GetError(); + LDAPDebug( LDAP_DEBUG_ANY, "PR_CreatePipe() failed, " + SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n", + prerr, slapd_pr_strerror(prerr), SLAPD_DEFAULT_THREAD_STACKSIZE ); + return( -1 ); + } + writesignalpipe = PR_FileDesc2NativeHandle(signalpipe[1]); + readsignalpipe = PR_FileDesc2NativeHandle(signalpipe[0]); +#else + if ( PR_CreatePipe( &signalpipe[0], &signalpipe[1] ) != 0 ) { + PRErrorCode prerr = PR_GetError(); + LDAPDebug( LDAP_DEBUG_ANY, "PR_CreatePipe() failed, " + SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n", + prerr, slapd_pr_strerror(prerr), SLAPD_DEFAULT_THREAD_STACKSIZE ); + return( -1 ); + } + writesignalpipe = PR_FileDesc2NativeHandle(signalpipe[1]); + readsignalpipe = PR_FileDesc2NativeHandle(signalpipe[0]); + fcntl(writesignalpipe, F_SETFD, O_NONBLOCK); + fcntl(readsignalpipe, F_SETFD, O_NONBLOCK); +#endif + + return( 0 ); +} + + +#ifdef HPUX10 +#include <pthread.h> /* for sigwait */ +/* + * Set up a thread to catch signals + * SIGUSR1 (ignore), SIGCHLD (call slapd_wait4child), + * SIGUSR2 (set slapd_shutdown), SIGTERM (set slapd_shutdown), + * SIGHUP (set slapd_shutdown) + */ +static void * +catch_signals() +{ + sigset_t caught_signals; + int sig; + + sigemptyset( &caught_signals ); + + while ( !g_get_shutdown() ) { + + /* Set the signals we're interested in catching */ + sigaddset( &caught_signals, SIGUSR1 ); + sigaddset( &caught_signals, SIGCHLD ); + sigaddset( &caught_signals, SIGUSR2 ); + sigaddset( &caught_signals, SIGTERM ); + sigaddset( &caught_signals, SIGHUP ); + + (void)sigprocmask( SIG_BLOCK, &caught_signals, NULL ); + + if (( sig = sigwait( &caught_signals )) < 0 ) { + LDAPDebug( LDAP_DEBUG_ANY, "catch_signals: sigwait returned -1\n", + 0, 0, 0 ); + continue; + } else { + LDAPDebug( LDAP_DEBUG_TRACE, "catch_signals: detected signal %d\n", + sig, 0, 0 ); + switch ( sig ) { + case SIGUSR1: + continue; /* ignore SIGUSR1 */ + case SIGUSR2: /* fallthrough */ + case SIGTERM: /* fallthrough */ + case SIGHUP: + g_set_shutdown( SLAPI_SHUTDOWN_SIGNAL ); + return NULL; + case SIGCHLD: + slapd_wait4child( sig ); + break; + default: + LDAPDebug( LDAP_DEBUG_ANY, + "catch_signals: unknown signal (%d) received\n", + sig, 0, 0 ); + } + } + } +} +#endif /* HPUX */ + +static int +get_configured_connection_table_size() +{ + int size; + size = config_get_conntablesize(); + +/* + * Cap the table size at nsslapd-maxdescriptors. + */ +#if !defined(_WIN32) && !defined(AIX) + { + int maxdesc = config_get_maxdescriptors(); + + if ( maxdesc >= 0 && size > maxdesc ) { + size = maxdesc; + } + } +#endif + + return size; +} + + + + +PRFileDesc * get_ssl_listener_fd() +{ + PRFileDesc * listener; + + listener = the_connection_table->fd[FDS_S_TCPS].fd; + + return listener; +} + + + +int configure_pr_socket( PRFileDesc **pr_socket, int secure ) +{ + int ns = 0; + int reservedescriptors = config_get_reservedescriptors(); + int ioblock_timeout = config_get_ioblocktimeout(); + int enable_nagle = config_get_nagle(); + + PRSocketOptionData pr_socketoption; + +#if defined(LINUX) + /* On Linux we use TCP_CORK so we must enable nagle */ + enable_nagle = 1; +#endif + + ns = PR_FileDesc2NativeHandle( *pr_socket ); + +#if !defined(_WIN32) + /* + * Some OS or third party libraries may require that low + * numbered file descriptors be available, e.g., the DNS resolver + * library on most operating systems. Therefore, we try to + * replace the file descriptor returned by accept() with a + * higher numbered one. If this fails, we log an error and + * continue (not considered a truly fatal error). + */ + if ( reservedescriptors > 0 && ns < reservedescriptors ) { + int newfd = fcntl( ns, F_DUPFD, reservedescriptors ); + + if ( newfd > 0 ) { + PRFileDesc *nspr_layer_fd = PR_GetIdentitiesLayer( *pr_socket, + PR_NSPR_IO_LAYER ); + if ( NULL == nspr_layer_fd ) { + slapi_log_error( SLAPI_LOG_FATAL, "configure_pr_socket", + "Unable to move socket file descriptor %d above %d:" + " PR_GetIdentitiesLayer( 0x%x, PR_NSPR_IO_LAYER )" + " failed\n", ns, reservedescriptors, *pr_socket ); + close( newfd ); /* can't fix things up in NSPR -- close copy */ + } else { + PR_ChangeFileDescNativeHandle( nspr_layer_fd, newfd ); + close( ns ); /* dup succeeded -- close the original */ + ns = newfd; + } + } else { + int oserr = errno; + slapi_log_error(SLAPI_LOG_FATAL, "configure_pr_socket", + "Unable to move socket file descriptor %d above %d:" + " OS error %d (%s)\n", ns, reservedescriptors, oserr, + slapd_system_strerror( oserr ) ); + } + } +#endif /* !_WIN32 */ + + if ( secure ) { + + pr_socketoption.option = PR_SockOpt_Nonblocking; + pr_socketoption.value.non_blocking = 0; + if ( PR_SetSocketOption( *pr_socket, &pr_socketoption ) == PR_FAILURE ) { + PRErrorCode prerr = PR_GetError(); + LDAPDebug( LDAP_DEBUG_ANY, + "PR_SetSocketOption(PR_SockOpt_Nonblocking) failed, " + SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n", + prerr, slapd_pr_strerror(prerr), 0 ); + } + } else { + /* We always want to have non-blocking I/O */ + pr_socketoption.option = PR_SockOpt_Nonblocking; + pr_socketoption.value.non_blocking = 1; + if ( PR_SetSocketOption( *pr_socket, &pr_socketoption ) == PR_FAILURE ) { + PRErrorCode prerr = PR_GetError(); + LDAPDebug( LDAP_DEBUG_ANY, + "PR_SetSocketOption(PR_SockOpt_Nonblocking) failed, " + SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n", + prerr, slapd_pr_strerror(prerr), 0 ); + } + + if ( have_send_timeouts ) { + daemon_configure_send_timeout(ns,config_get_ioblocktimeout()); + } + + } /* else (secure) */ + + + if ( !enable_nagle ) { + + pr_socketoption.option = PR_SockOpt_NoDelay; + pr_socketoption.value.no_delay = 1; + if ( PR_SetSocketOption( *pr_socket, &pr_socketoption ) == PR_FAILURE) { + PRErrorCode prerr = PR_GetError(); + LDAPDebug( LDAP_DEBUG_ANY, + "PR_SetSocketOption(PR_SockOpt_NoDelay) failed, " + SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n", + prerr, slapd_pr_strerror( prerr ), 0 ); + } + } else { + pr_socketoption.option = PR_SockOpt_NoDelay; + pr_socketoption.value.no_delay = 0; + if ( PR_SetSocketOption( *pr_socket, &pr_socketoption ) == PR_FAILURE) { + PRErrorCode prerr = PR_GetError(); + LDAPDebug( LDAP_DEBUG_ANY, + "PR_SetSocketOption(PR_SockOpt_NoDelay) failed, " + SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n", + prerr, slapd_pr_strerror( prerr ), 0 ); + } + } /* else (!enable_nagle) */ + + + return ns; + +} + + + + +void configure_ns_socket( int * ns ) +{ + + int ioblock_timeout = config_get_ioblocktimeout(); + int enable_nagle = config_get_nagle(); + int on; + +#if defined(LINUX) + /* On Linux we use TCP_CORK so we must enable nagle */ + enable_nagle = 1; +#endif + + if ( have_send_timeouts ) { + daemon_configure_send_timeout( *ns, config_get_ioblocktimeout() ); + } + + + if ( !enable_nagle ) { + on = 1; + setsockopt( *ns, IPPROTO_TCP, TCP_NODELAY, (char * ) &on, sizeof(on) ); + } else { + on = 0; + setsockopt( *ns, IPPROTO_TCP, TCP_NODELAY, (char * ) &on, sizeof(on) ); + } /* else (!enable_nagle) */ + + + return; + +} + + +#ifdef RESOLVER_NEEDS_LOW_FILE_DESCRIPTORS +/* + * A function that uses the DNS resolver in a simple way. This is only + * used to ensure that the DNS resolver has opened its files, etc. + * using low numbered file descriptors. + */ +static void +get_loopback_by_addr( void ) +{ +#ifdef GETHOSTBYADDR_BUF_T + struct hostent hp; + GETHOSTBYADDR_BUF_T hbuf; +#endif + unsigned long ipaddr; + struct in_addr ia; + int herrno, rc = 0; + + memset( (char *)&hp, 0, sizeof(hp)); + ipaddr = htonl( INADDR_LOOPBACK ); + (void) GETHOSTBYADDR( (char *)&ipaddr, sizeof( ipaddr ), + AF_INET, &hp, hbuf, sizeof(hbuf), &herrno ); +} +#endif /* RESOLVER_NEEDS_LOW_FILE_DESCRIPTORS */ |