Newsgroups: comp.sources.unix From: panos@cs.colorado.edu (Panos Tsirigotis) Subject: v26i271: xinetd-2.1.1 - inetd replacement with access control and logging, Part27/31 Sender: unix-sources-moderator@gw.home.vix.com Approved: vixie@gw.home.vix.com Submitted-By: panos@cs.colorado.edu (Panos Tsirigotis) Posting-Number: Volume 26, Issue 271 Archive-Name: xinetd-2.1.1/part27 #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh 'xinetd/service.c' <<'END_OF_FILE' X/* X * (c) Copyright 1992 by Panagiotis Tsirigotis X * All rights reserved. The file named COPYRIGHT specifies the terms X * and conditions for redistribution. X */ X Xstatic char RCSid[] = "$Id: service.c,v 6.12 1993/06/13 01:46:35 panos Exp $" ; X X#include X#include X#include X#include X#include X#include X X#include "sio.h" X X#include "options.h" X X#include "access.h" X#include "attr.h" X#include "service.h" X#include "server.h" X#include "state.h" X#include "config.h" X#include "connection.h" X X#define NEW_SVC() NEW( struct service ) X#define FREE_SVC( sp ) FREE( sp ) X X#define SUSPEND( sp ) (sp)->svc_state = SVC_SUSPENDED X#define RESUME( sp ) (sp)->svc_state = SVC_ACTIVE X#define DISABLE( sp ) (sp)->svc_state = SVC_DISABLED X X Xvoid msg() ; Xvoid out_of_memory() ; X Xtime_t time() ; Xchar *malloc() ; X Xstatic struct name_value service_states[] = X { X { "Not started", (int) SVC_NOT_STARTED }, X { "Active", (int) SVC_ACTIVE }, X { "Disabled", (int) SVC_DISABLED }, X { "Suspended", (int) SVC_SUSPENDED }, X { NULL, 1 }, X { "BAD STATE", 0 } X } ; X X X X/* X * Allocate a new struct service and initialize it from scp X */ Xstruct service *svc_new( scp ) X struct service_config *scp ; X{ X struct service *sp ; X char *func = "svc_new" ; X X sp = NEW_SVC() ; X if ( sp == NULL ) X { X out_of_memory( func ) ; X return( NULL ) ; X } X CLEAR( *sp ) ; X X sp->svc_conf = scp ; X return( sp ) ; X} X X Xstruct service *svc_make_special( scp, handler ) X struct service_config *scp ; X statfunc handler ; X{ X struct service *sp ; X char *func = "svc_make_special" ; X X if ( ( sp = svc_new( scp ) ) == NULL ) X { X out_of_memory( func ) ; X return( NULL ) ; X } X X sp->svc_handler_func = handler ; X sp->svc_log = ps.rws.program_log ; X X sp->svc_ref_count = 1 ; X sp->svc_state = SVC_ACTIVE ; X return( sp ) ; X} X X Xvoid svc_free( sp ) X struct service *sp ; X{ X sc_free( sp->svc_conf ) ; X FREE_SVC( sp ) ; X} X X X/* X * Currently, address control works by looking at the fields X * only_from/no_access of struct service. X * In order to enable address control, we copy to these fields either X * the explicitly specified values (which come from the service_config) X * or the default ones (if there are any). X */ Xvoid svc_setup_address_control( sp ) X register struct service *sp ; X{ X register struct service_config *scp = SVC_CONF( sp ) ; X X if ( SC_IS_PRESENT( scp, A_ONLY_FROM ) ) X sp->svc_only_from = SC_ONLY_FROM( scp ) ; X else if ( SC_SPECIFIED( DEFAULTS( ps ), A_ONLY_FROM ) ) X sp->svc_only_from = SC_ONLY_FROM( DEFAULTS( ps ) ) ; X else X sp->svc_only_from = NULL ; X X if ( SC_IS_PRESENT( scp, A_NO_ACCESS ) ) X sp->svc_no_access = SC_NO_ACCESS( scp ) ; X else if ( SC_SPECIFIED( DEFAULTS( ps ), A_NO_ACCESS ) ) X sp->svc_no_access = SC_NO_ACCESS( DEFAULTS( ps ) ) ; X else X sp->svc_no_access = NULL ; X} X X XPRIVATE status_e set_fd_modes( sp ) X register struct service *sp ; X{ X int sd = SVC_FD( sp ) ; X char *func = "set_fd_modes" ; X X /* X * There is a possibility of blocking on a send/write if X * X * the service does not require forking (==> is internal) AND X * it does not accept connections X * X * To avoid this, we put the descriptor in FNDELAY mode. X * (if the service accepts connections, we still need to put the X * 'accepted' connection in FNDELAY mode but this is done elsewhere) X */ X if ( ! SVC_FORKS( sp ) && ! SVC_ACCEPTS_CONNECTIONS( sp ) && X fcntl( sd, F_SETFL, FNDELAY ) == -1 ) X { X msg( LOG_ERR, func, X "fcntl failed (%m) for FNDELAY. service = %s", SVC_ID( sp ) ) ; X return( FAILED ) ; X } X X /* X * Always set the close-on-exec flag X */ X if ( fcntl( sd, F_SETFD, 1 ) == -1 ) X { X msg( LOG_ERR, func, X "fcntl failed (%m) for close-on-exec. service = %s", SVC_ID( sp ) ) ; X return( FAILED ) ; X } X return( OK ) ; X} X X X#ifndef NO_RPC X XPRIVATE status_e activate_rpc( sp ) X register struct service *sp ; X{ X struct sockaddr_in sin ; X int sin_len ; X unsigned long vers ; X struct service_config *scp = SVC_CONF( sp ) ; X struct rpc_data *rdp = SC_RPCDATA( scp ) ; X char *sid = SC_ID( scp ) ; X unsigned registered_versions = 0 ; X int sd = SVC_FD( sp ) ; X char *func = "activate_rpc" ; X X CLEAR( sin ) ; X X sin.sin_family = AF_INET ; X sin.sin_addr.s_addr = htonl( INADDR_ANY ) ; X sin.sin_port = 0 ; /* let the system give us a port */ X if ( bind( sd, SA( &sin ), sizeof( sin ) ) == -1 ) X { X msg( LOG_ERR, func, "bind failed (%m). service = %s", sid ) ; X return( FAILED ) ; X } X X /* X * Find the port number that was assigned to the socket X */ X sin_len = sizeof( sin ) ; X if ( getsockname( sd, SA( &sin ), &sin_len ) == -1 ) X { X msg( LOG_ERR, func, X "getsockname failed (%m). service = %s", sid ) ; X return( FAILED ) ; X } X sc_set_port( scp, ntohs( sin.sin_port ) ) ; X X /* X * Try to register as many versions as possible X */ X for ( vers = RD_MINVERS( rdp ) ; vers <= RD_MAXVERS( rdp ) ; vers++ ) X if ( pmap_set( RD_PROGNUM( rdp ), vers, X SC_PROTOVAL( scp ), SC_PORT( scp ) ) ) X registered_versions++ ; X else X msg( LOG_ERR, func, X "pmap_set failed. service=%s program=%ld version=%ld", X sid, RD_PROGNUM( rdp ), vers ) ; X X if ( debug.on ) X msg( LOG_DEBUG, func, X "Registered %d versions of %s", registered_versions, sid ) ; X X return( ( registered_versions == 0 ) ? FAILED : OK ) ; X} X X#endif /* ! NO_RPC */ X X XPRIVATE status_e activate_normal( sp ) X register struct service *sp ; X{ X struct sockaddr_in sin ; X int sd = SVC_FD( sp ) ; X struct service_config *scp = SVC_CONF( sp ) ; X unsigned short service_port = SC_PORT( scp ) ; X char *sid = SC_ID( scp ) ; X char *func = "activate_normal" ; X X CLEAR( sin ) ; X X sin.sin_family = AF_INET ; X sin.sin_addr.s_addr = htonl( INADDR_ANY ) ; X sin.sin_port = htons( service_port ) ; X X if ( reuse_option || SC_REUSE_ADDRESS( scp ) ) X { X int on = 1 ; X X if ( setsockopt( sd, SOL_SOCKET, X SO_REUSEADDR, (char *) &on, sizeof( on ) ) == -1 ) X msg( LOG_WARNING, func, X "setsockopt SO_REUSEADDR failed (%m). service = %s", sid ) ; X } X X if ( bind( sd, SA( &sin ), sizeof( sin ) ) == -1 ) X { X msg( LOG_ERR, func, "bind failed (%m). service = %s", sid ) ; X return( FAILED ) ; X } X return( OK ) ; X} X X X/* X * Activate a service. X */ Xstatus_e svc_activate( sp ) X register struct service *sp ; X{ X struct service_config *scp = SVC_CONF( sp ) ; X status_e status ; X char *func = "svc_activate" ; X voidfunc get_shutdown_by_name() ; X PRIVATE status_e svc_generic_handler() ; X PRIVATE void deactivate() ; X X sp->svc_fd = socket( AF_INET, X SC_SOCKET_TYPE( scp ), SC_PROTOVAL( scp ) ) ; X X if ( sp->svc_fd == -1 ) X { X msg( LOG_ERR, func, X "socket creation failed (%m). service = %s", SC_ID( scp ) ) ; X return( FAILED ) ; X } X X if ( set_fd_modes( sp ) == FAILED ) X { X (void) close( sp->svc_fd ) ; X return( FAILED ) ; X } X X#ifndef NO_RPC X if ( SC_IS_RPC( scp ) ) X status = activate_rpc( sp ) ; X else X#endif /* ! NO_RPC */ X status = activate_normal( sp ) ; X X if ( status == FAILED ) X { X (void) close( sp->svc_fd ) ; X return( FAILED ) ; X } X X if ( log_start( sp, &sp->svc_log ) == FAILED ) X { X deactivate( sp ) ; X return( FAILED ) ; X } X X /* X * Initialize the service data X */ X sp->svc_running_servers = sp->svc_retry_servers = 0 ; X sp->svc_shutdown_func = get_shutdown_by_name( SC_NAME( scp ) ) ; X sp->svc_handler_func = svc_generic_handler ; X svc_setup_address_control( sp ) ; X X if ( SC_MUST_LISTEN( scp ) ) X (void) listen( sp->svc_fd, LISTEN_BACKLOG ) ; X X ps.rws.descriptors_free-- ; X X sp->svc_state = SVC_ACTIVE ; X X FD_SET( sp->svc_fd, &ps.rws.socket_mask ) ; X if ( sp->svc_fd > ps.rws.mask_max ) X ps.rws.mask_max = sp->svc_fd ; X X ps.rws.active_services++ ; X ps.rws.available_services++ ; X X return( OK ) ; X} X X XPRIVATE void deactivate( sp ) X register struct service *sp ; X{ X (void) close( SVC_FD( sp ) ) ; X X#ifndef NO_RPC X if ( SC_IS_RPC( SVC_CONF( sp ) ) ) X { X register unsigned long vers ; X register struct rpc_data *rdp = SC_RPCDATA( SVC_CONF( sp ) ) ; X X for ( vers = RD_MINVERS( rdp ) ; vers <= RD_MAXVERS( rdp ) ; vers++ ) X (void) pmap_unset( RD_PROGNUM( rdp ), vers ) ; X } X#endif /* ! NO_RPC */ X} X X X/* X * Close the service descriptor. X * If this is an RPC service, deregister it. X * Close the log. X */ Xvoid svc_deactivate( sp ) X register struct service *sp ; X{ X if ( ! SVC_IS_AVAILABLE( sp ) ) X return ; X X deactivate( sp ) ; X ps.rws.descriptors_free++ ; X X if ( sp->svc_log ) X log_end( SC_LOG( SVC_CONF( sp ) ), sp->svc_log ) ; X X if ( SVC_IS_ACTIVE( sp ) ) X { X FD_CLR( SVC_FD( sp ), &ps.rws.socket_mask ) ; X ps.rws.active_services-- ; X } X X ps.rws.available_services-- ; X X DISABLE( sp ) ; X} X X X X/* X * Suspend a service X */ Xvoid svc_suspend( sp ) X register struct service *sp ; X{ X char *func = "svc_suspend" ; X X if ( ! SVC_IS_ACTIVE( sp ) ) X { X msg( LOG_ERR, func, "service %s is not active", SVC_ID( sp ) ) ; X return ; X } X X FD_CLR( SVC_FD( sp ), &ps.rws.socket_mask ) ; X ps.rws.active_services-- ; X if ( debug.on ) X msg( LOG_DEBUG, func, "Suspended service %s", SVC_ID( sp ) ) ; X X SUSPEND( sp ) ; X} X X X/* X * Resume a suspended service. X */ Xvoid svc_resume( sp ) X register struct service *sp ; X{ X char *func = "svc_resume" ; X X FD_SET( SVC_FD( sp ), &ps.rws.socket_mask ) ; X ps.rws.active_services++ ; X if ( debug.on ) X msg( LOG_DEBUG, func, "Resumed service %s", SVC_ID( sp ) ) ; X RESUME( sp ) ; X} X X X/* X * Steps: X * 1. Deactivate the service X * 2. Free all memory used by the service and free the service itself X * X * Since this function may free all memory associated with the service as X * well as the memory pointed by sp, only the value of sp should be used X * after this call if the return value is 0 (i.e. no dereferencing of sp). X * X * Special services are never deactivated. X */ Xint svc_release( sp ) X register struct service *sp ; X{ X char *sid = SVC_ID( sp ) ; X char *func = "svc_release" ; X X if ( sp->svc_ref_count == 0 ) X { X msg( LOG_ERR, func, "%s: svc_release with 0 count", sid ) ; X return( 0 ) ; X } X X sp->svc_ref_count-- ; X if ( sp->svc_ref_count == 0 ) X { X if ( debug.on ) X msg( LOG_DEBUG, func, "ref count of service %s dropped to 0", sid ) ; X if ( ! SC_IS_SPECIAL( SVC_CONF( sp ) ) ) X { X svc_deactivate( sp ) ; X svc_free( sp ) ; X } X else /* this shouldn't happen */ X msg( LOG_WARNING, func, X "ref count of special service %s dropped to 0", sid ) ; X return( 0 ) ; X } X else X return( sp->svc_ref_count ) ; X} X X X Xvoid svc_dump( sp, fd ) X register struct service *sp ; X int fd ; X{ X char *get_shutdown_by_addr() ; X void tabprint() ; X X tabprint( fd, 0, "Service = %s\n", SC_NAME( SVC_CONF( sp ) ) ) ; X tabprint( fd, 1, "State = %s\n", X nv_get_name( service_states, (int) sp->svc_state ) ) ; X X sc_dump( SVC_CONF( sp ), fd, 1, FALSE ) ; X X if ( sp->svc_state == SVC_ACTIVE ) X { X tabprint( fd, 1, "running servers = %d\n", sp->svc_running_servers ) ; X tabprint( fd, 1, "retry servers = %d\n", sp->svc_retry_servers ) ; X tabprint( fd, 1, "attempts = %d\n", sp->svc_attempts ) ; X tabprint( fd, 1, "service fd = %d\n", sp->svc_fd ) ; X tabprint( fd, 1, "shutdown function = %s\n", X get_shutdown_by_addr( sp->svc_shutdown_func ) ) ; X } X Sputchar( fd, '\n' ) ; X} X X X/* X * Returns TRUE if the server instantiation rate is over the limit X */ Xbool_int svc_looping( sp ) X register struct service *sp ; X{ X time_t current_time ; X register time_t time_diff ; X char *func = "svc_looping" ; X X (void) time( ¤t_time ) ; X X if ( sp->svc_attempts == 0 ) X { X sp->svc_attempts++ ; X sp->svc_start_time = current_time ; X return( FALSE ) ; X } X X time_diff = current_time - sp->svc_start_time ; X if ( time_diff <= LOOP_INTERVAL ) X { X sp->svc_attempts++ ; X if ( time_diff == 0 ) X time_diff = 1 ; X if ( sp->svc_attempts/time_diff > ps.ros.loop_rate ) X { X FD_CLR( SVC_FD( sp ), &ps.rws.socket_mask ) ; X svc_deactivate( sp ) ; X msg( LOG_ERR, func, X "%s service was deactivated because of looping", SVC_ID( sp ) ) ; X return( TRUE ) ; X } X } X else X { X sp->svc_start_time = current_time ; X sp->svc_attempts = 1 ; X } X return( FALSE ) ; X} X X X Xvoid svc_request( sp ) X register struct service *sp ; X{ X register connection_s *cp ; X X cp = conn_new( sp ) ; X if ( cp == CONN_NULL ) X return ; X X if ( svc_handle( sp, cp ) == OK ) X return ; X else X { X conn_cleanup( cp ) ; X if ( conn_start_alternative( cp ) == FAILED ) X conn_free( cp ) ; X } X} X X X XPRIVATE status_e svc_generic_handler( sp, cp ) X register struct service *sp ; X register connection_s *cp ; X{ X register struct service_config *scp = SVC_CONF( sp ) ; X X /* X * There is no loop check for services that accept connections because X * the connections are accepted by xinetd, therefore the queue will get X * empty eventually even if the server is faulty. X * Services for which no fork(2) occurs are not checked because X * a) they are internal and we assume they are not faulty X * b) we have no way of determining if something is going wrong X * for example, UDP discard can do its job very quickly X */ X if ( !SC_ACCEPTS_CONNECTIONS( scp ) && SVC_FORKS( sp ) && svc_looping( sp ) ) X return( FAILED ) ; X X if ( svc_access_control( sp, cp ) == OK ) X return( server_run( sp, cp ) ) ; X X /* X * Add alternative services to handle the connection attempt. X * The services must be added in the proper order because they X * are invoked in that order: X * logging X * shutdown X */ X X if ( SVC_LOGS_USERID_ON_FAILURE( sp ) ) X (void) conn_add_alternative( cp, LOG_SERVICE( ps ) ) ; X X if ( sp->svc_shutdown_func != NULL ) X { X if ( ! SVC_RECORDS( sp ) || X conn_add_alternative( cp, SHUTDOWN_SERVICE( ps ) ) == FAILED ) X conn_shutdown( cp ) ; X } X X return( FAILED ) ; X} X X Xstatus_e svc_access_control( sp, cp ) X register struct service *sp ; X connection_s *cp ; X{ X access_e result ; X X result = access_control( sp, cp, MASK_NULL ) ; X if ( result != AC_OK ) X { X bool_int report_failure = TRUE ; X X /* X * Try to avoid reporting multiple times a failed attempt to access X * a datagram-based service from a bad address. We do this because X * the clients of such services usually send multiple datagrams X * before reporting a timeout (we have no way of telling them that X * their request has been denied). X */ X if ( result == AC_ADDRESS && SVC_SOCKET_TYPE( sp ) == SOCK_DGRAM ) X { X register struct sockaddr_in *sinp = conn_address( cp ) ; X register struct sockaddr_in *last = &sp->svc_last_dgram_addr ; X time_t current_time ; X X (void) time( ¤t_time ) ; X X if ( sinp->sin_addr.s_addr == last->sin_addr.s_addr && X sinp->sin_port == last->sin_port ) X { X if ( current_time - sp->svc_last_dgram_time <= DGRAM_IGNORE_TIME ) X report_failure = FALSE ; X else X sp->svc_last_dgram_time = current_time ; X } X else X { X sp->svc_last_dgram_addr = *sinp ; X sp->svc_last_dgram_time = current_time ; X } X } X X if ( report_failure ) X svc_log_failure( sp, cp, result ) ; X return( FAILED ) ; X } X return( OK ) ; X} X X X/* X * Implement shutdown protocol (usually sends an error indication). X */ Xvoid svc_shutdown( sp, cp, msgp ) X register struct service *sp ; X register connection_s *cp ; X char **msgp ; X{ X int old_flags ; X register bool_int no_block ; X int fd = CONN_DESCRIPTOR( cp ) ; X char *func = "svc_shutdown" ; X X#ifdef DEBUG X if ( sp != CONN_SERVICE( cp ) ) X { X msg( LOG_CRIT, func, "service = %s, service of connection = %s", X SVC_ID( sp ), SVC_ID( CONN_SERVICE( cp ) ) ) ; X return ; X } X#endif X X if ( sp->svc_shutdown_func == NULL ) X return ; X X no_block = ( msgp == NULL ) ; X X /* X * Ensure that we won't block in the shutdown function X */ X if ( no_block ) X { X if ( ( old_flags = fcntl( fd, F_GETFL, 0 ) ) == -1 ) X { X msg( LOG_ERR, func, "fcntl-getflags failed: %m" ) ; X return ; X } X X if ( fcntl( fd, F_SETFL, FNDELAY ) == -1 ) X { X msg( LOG_ERR, func, "fcntl-setflags failed: %m" ) ; X return ; X } X } X X (*sp->svc_shutdown_func)( fd, msgp ) ; X X /* X * Don't bother with restoring the flags if this is a new descriptor X */ X if ( no_block && SC_ACCEPTS_CONNECTIONS( SVC_CONF( sp ) ) ) X (void) fcntl( fd, F_SETFL, old_flags ) ; X} X X X/* X * Invoked when a server of the specified service dies X */ Xvoid svc_postmortem( sp, serp ) X register struct service *sp ; X register struct server *serp ; X{ X struct service *co_sp = SERVER_CONNSERVICE( serp ) ; X register connection_s *cp = SERVER_CONNECTION( serp ) ; X char *func = "svc_postmortem" ; X X svc_dec_running_servers( sp ) ; X X /* X * Log information about the server that died X */ X if ( SVC_IS_LOGGING( sp ) ) X { X if ( serp->svr_writes_to_log ) X { X if ( debug.on ) X msg( LOG_DEBUG, func, X "Checking log size of %s service", SVC_ID( sp ) ) ; X xlog_control( SVC_LOG( sp ), XLOG_SIZECHECK ) ; X } X svc_log_exit( sp, serp ) ; X } X X /* X * Check for alternative services to handle this connection X */ X if ( conn_start_alternative( cp ) == OK ) X return ; X X /* X * Now check if we have to check the log size of the service that owns X * the connection X */ X if ( co_sp != sp && SVC_IS_LOGGING( co_sp ) ) X xlog_control( SVC_LOG( co_sp ), XLOG_SIZECHECK ) ; X X conn_free( cp ) ; X} X END_OF_FILE if test 17419 -ne `wc -c <'xinetd/service.c'`; then echo shar: \"'xinetd/service.c'\" unpacked with wrong size! fi # end of 'xinetd/service.c' fi echo shar: End of archive 27 \(of 31\). cp /dev/null ark27isdone MISSING="" for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 31 archives. rm -f ark[1-9]isdone ark[1-9][0-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0