/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Sections Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #include #include #include #include #include #include #include #include #include "intcommon.h" #include "msg.h" #include "signals.h" #include "connection.h" #include "sconf.h" #include "state.h" #include "main.h" #include "xconfig.h" void int_fail( const struct intercept_s *ip, const char *lsyscall ) { msg( LOG_ERR, "fail", "%s failed: %m", lsyscall ) ; (*ip->int_ops->exit)() ; /* NOTREACHED */ } /* * Returns either a positive number or -1 */ int int_select( int max, fd_set *read_mask ) { const char *func = "int_select" ; for ( ;; ) { int n_ready ; n_ready = select( max+1, read_mask, FD_SET_NULL, FD_SET_NULL, TIMEVAL_NULL ) ; if ( n_ready > 0 ) return( n_ready ) ; else if ( n_ready == -1 ) { if ( errno == EINTR ) continue ; else { msg( LOG_ERR, func, "select: %m" ) ; return( -1 ) ; } } } } void int_exit( struct intercept_s *ip ) { int status = SERVER_EXITSTATUS( INT_SERVER( ip ) ) ; const char *func = "int_exit" ; if ( debug.on ) { if ( PROC_EXITED( status ) ) msg( LOG_DEBUG, func, "intercepted server died" ) ; else if ( PROC_SIGNALED( status ) ) msg( LOG_DEBUG, func, "intercepted server received signal %s", sig_name( (int) PROC_TERMSIG( status ) ) ) ; } _exit( (int) PROC_EXITSTATUS( status ) ) ; } /* * The ops vector must be installed before invoking this function */ void int_init( struct intercept_s *ip, struct server *serp ) { unsigned u ; const char *func = "int_init" ; /* * Sanity test */ if ( SERVER_SERVICE( serp ) != SERVER_CONNSERVICE( serp ) ) { msg( LOG_ERR, func, "server service (%s) != connection service (%s)", SVC_ID( SERVER_SERVICE( serp ) ), SVC_ID( SERVER_CONNSERVICE( serp ) ) ) ; exit( 1 ) ; } /* * Close all unneeded descriptors */ for ( u = 0 ; u < pset_count( SERVICES( ps ) ) ; u++ ) { struct service *sp = SP( pset_pointer( SERVICES( ps ), u ) ) ; if ( sp == SERVER_SERVICE( serp ) ) continue ; if ( LOG_GET_TYPE( SC_LOG( SVC_CONF( sp ) ) ) == L_FILE ) xlog_destroy( SVC_LOG( sp ) ) ; (void) Sclose( SVC_FD( sp ) ) ; } /* * Setup signal handling */ if ( signal( SERVER_EXIT_SIG, int_sighandler ) == SIG_ERR ) int_fail( ip, "signal" ) ; if ( signal( INTERCEPT_SIG, int_sighandler ) == SIG_ERR ) int_fail( ip, "signal" ) ; if ( signal( SIGTERM, int_sighandler ) == SIG_ERR ) int_fail( ip, "signal" ) ; /* * Initialize state */ INTERCEPT( ip ) = TRUE ; *INT_SERVER( ip ) = *serp ; INT_REMOTE( ip ) = SERVER_FD( serp ) ; INT_CONNECTIONS( ip ) = pset_create( 0, 0 ) ; if ( INT_CONNECTIONS( ip ) == NULL ) { msg( LOG_ERR, func, ES_NOMEM ) ; (*ip->int_ops->exit)() ; } } /* * Make a new connection to the local server */ channel_s *int_newconn( struct intercept_s *ip, union xsockaddr *sinp, int remote_socket ) { struct service *sp = SERVER_SERVICE( INT_SERVER( ip ) ) ; int socket_type = SVC_SOCKET_TYPE( sp ) ; union xsockaddr *local = INT_LOCALADDR( ip ) ; char *sid = SVC_ID( sp ) ; channel_s *chp ; int sd ; const char *func = "int_newconn" ; /* * Get a socket and connect it to the local address * */ if ( ( sd = socket( local->sa.sa_family, socket_type, SC_PROTOVAL(SVC_CONF(sp)) ) ) == -1 ) { msg( LOG_ERR, func,"(intercepting %s) socket creation failed: %m", sid ) ; return( CHANNEL_NULL ) ; } if ( connect( sd, SA( local ), sizeof( *local ) ) == -1 ) { msg( LOG_ERR, func, "(intercepting %s) connect failed: %m", sid ) ; (void) Sclose( sd ) ; return( CHANNEL_NULL ) ; } chp = NEW_CHANNEL() ; if ( chp == CHANNEL_NULL ) { msg( LOG_ERR, func, ES_NOMEM ) ; (void) Sclose( sd ) ; return( CHANNEL_NULL ) ; } if ( pset_add( INT_CONNECTIONS( ip ), chp ) == NULL ) { msg( LOG_ERR, func, ES_NOMEM ) ; FREE_CHANNEL( chp ) ; (void) Sclose( sd ) ; return( CHANNEL_NULL ) ; } chp->ch_state = GOOD_CHANNEL ; chp->ch_from = *sinp ; chp->ch_local_socket = sd ; chp->ch_remote_socket = remote_socket ; return( chp ) ; } /* * Check if the (address,port) in sinp is already in the connection table. * Return value: * a connection pointer if the address is found * NULL if the address if not found * * *addr_checked is set to TRUE of FALSE depending on whether there * is already a connection from the same IP address in the table. */ channel_s *int_lookupconn( struct intercept_s *ip, union xsockaddr *sinp, bool_int *addr_checked ) { unsigned u ; pset_h conntab = INT_CONNECTIONS( ip ) ; *addr_checked = FALSE ; for ( u = 0 ; u < pset_count( conntab ) ; u++ ) { register channel_s *chp = CHP( pset_pointer( conntab, u ) ) ; if ( memcmp( &chp->ch_from, sinp, sizeof( *sinp ) ) == 0 ) { *addr_checked = TRUE ; if ( xaddrport(&chp->ch_from) == xaddrport(sinp) ) return( chp ) ; } } return( CHANNEL_NULL ) ; }