Пример #1
0
static void dgram_chargen( const struct server *serp )
{
   char            buf[ BUFFER_SIZE ] ;
   char            *p ;
   unsigned int    len ;
   union xsockaddr lsin ;
   socklen_t       sin_len = 0 ;
   int             fd      = SERVER_FD( serp ) ;
   unsigned int    left    = sizeof( buf ) ;
   const char     *func    = "dgram_chargen";

   if ( SC_IPV4( SVC_CONF( SERVER_SERVICE( serp ) ) ) ) 
      sin_len = sizeof( struct sockaddr_in );
   else if ( SC_IPV6( SVC_CONF( SERVER_SERVICE( serp ) ) ) ) 
      sin_len = sizeof( struct sockaddr_in6 );

   if ( recvfrom( fd, buf, sizeof( buf ), 0, SA( &lsin ), &sin_len ) == -1 )
      return ;

#if BUFFER_SIZE < LINE_LENGTH+2
   bad_variable = 1 ;      /* this will cause a compilation error */
#endif

   for ( p = buf ; left > 2 ; left -= len, p += len )
   {
      len = min( LINE_LENGTH+2, left ) ;
      if ( generate_line( p, len ) == NULL )
         break ;
   }
   (void) sendto( fd, buf, p-buf, 0, SA( &lsin ), sin_len ) ;
}
Пример #2
0
/*
 * Steps:
 *      1. Deactivate the service
 *      2. Free all memory used by the service and free the service itself
 *
 * Since this function may free all memory associated with the service as
 * well as the memory pointed by sp, only the value of sp should be used
 * after this call if the return value is 0 (i.e. no dereferencing of sp).
 *
 * Special services are never deactivated.
 */
int svc_release( struct service *sp )
{
   char *sid = SVC_ID( sp ) ;
   const char *func = "svc_release" ;

   if ( SVC_REFCOUNT(sp) == 0 )
   {
      msg( LOG_ERR, func, "%s: svc_release with 0 count", sid ) ;
      return( 0 ) ;
   }
   
   SVC_REFCOUNT(sp)-- ;
   if ( SVC_REFCOUNT(sp) == 0 )
   {
      if ( debug.on )
         msg( LOG_DEBUG, func, "ref count of service %s dropped to 0", sid ) ;
      if ( ! SC_IS_SPECIAL( SVC_CONF( sp ) ) )
      {
         if ( SVC_LOG(sp) )
            log_end( SC_LOG( SVC_CONF( sp ) ), SVC_LOG(sp) ) ;
         svc_deactivate( sp ) ;
         svc_free( sp ) ;
         sp = NULL;
      }
      else      /* this shouldn't happen */
         msg( LOG_WARNING, func,
            "ref count of special service %s dropped to 0", sid ) ;
      return( 0 ) ;
   }
   else
      return( SVC_REFCOUNT(sp) ) ;
}
Пример #3
0
static status_e get_incoming_packet( struct intercept_s *ip, packet_s *pp )
{
   socklen_t from_len = 0;
   const char *func = "get_incoming_packet" ;

   if( SC_IPV4( SVC_CONF( SERVER_SERVICE( INT_SERVER( ip ) ) ) ) )
      from_len = sizeof( struct sockaddr_in );
   if( SC_IPV6( SVC_CONF( SERVER_SERVICE( INT_SERVER( ip ) ) ) ) )
      from_len = sizeof( struct sockaddr_in6 );

   for ( ;; )
   {
      int cc ;

      from_len = sizeof( pp->from ) ;
      cc = recvfrom( INT_REMOTE( ip ), pp->data, pp->size,
                                    0, SA( &pp->from ), &from_len ) ;
      if ( cc == -1 )
      {
         if ( errno != EINTR )
         {
            msg( LOG_ERR, func, "recvfrom error: %m" ) ;
            return( FAILED ) ;
         }
      }
      else if ( cc == 0 )
         return( FAILED ) ;
      else
      {
         pp->size = cc ;
         IDP( ip->int_priv )->received_packets++ ;
         break ;
      }
   }

   if ( from_len == 0 )
   {
      msg( LOG_ERR, func, "incoming packet had 0 length address" ) ;
      return( FAILED ) ;
   }
   
#ifdef DEBUG_UDPINT
   if ( debug.on )
      msg( LOG_DEBUG, func, "Received %d bytes from address: %s,%d",
         pp->size, xaddrname( &pp->from ), ntohs( xaddrport(&pp->from) ) );
#endif

   return( OK ) ;
}
Пример #4
0
void svc_dump( const struct service *sp, int fd )
{
   tabprint( fd, 0, "Service = %s\n", SC_NAME( SVC_CONF( sp ) ) ) ;
   tabprint( fd, 1, "State = %s\n",
                        nv_get_name( service_states, (int) SVC_STATE(sp) ) ) ;

   sc_dump( SVC_CONF( sp ), fd, 1, FALSE ) ;

   if ( SVC_IS_ACTIVE(sp) )
   {
      tabprint( fd, 1, "running servers = %d\n", SVC_RUNNING_SERVERS(sp) ) ;
      tabprint( fd, 1, "retry servers = %d\n", SVC_RETRIES(sp) ) ;
      tabprint( fd, 1, "attempts = %d\n", SVC_ATTEMPTS(sp) ) ;
      tabprint( fd, 1, "service fd = %d\n", SVC_FD(sp) ) ;
   }
   Sputchar( fd, '\n' ) ;
}
Пример #5
0
void svc_request( struct service *sp )
{
   connection_s *cp ;
   status_e ret_code;

   cp = conn_new( sp ) ;
   if ( cp == CONN_NULL )
      return ;

   /*
    * Output the banner now that the connection is established. The
    * other banners come later.
    */
   banner_always(sp, cp);

   if (SVC_NOT_GENERIC(sp))
      ret_code = spec_service_handler(sp, cp);
   else 
      ret_code = svc_generic_handler(sp, cp);

   if( (SVC_SOCKET_TYPE( sp ) == SOCK_DGRAM) && (SVC_IS_ACTIVE( sp )) ) 
      drain( cp->co_descriptor ) ; /* Prevents looping next time */
   
   if ( ret_code != OK ) 
   {
      if ( SVC_LOGS_USERID_ON_FAILURE( sp ) ) {
         if( spec_service_handler( LOG_SERVICE( ps ), cp ) == FAILED ) 
	    conn_free( cp, 1 ) ;
         else if (!SC_WAITS( SVC_CONF( sp ) ) ) {
	 /* The logging service will gen SIGCHLD thus freeing connection */
	    CONN_CLOSE(cp) ; 
	 }
	 return;
      }
      if (!SC_WAITS( SVC_CONF( sp ) )) 
	 conn_free( cp, 1 );
      else { 
         if( (SVC_SOCKET_TYPE( sp ) == SOCK_DGRAM) && (SVC_IS_ACTIVE( sp )) ) 
            drain( cp->co_descriptor ) ; /* Prevents looping next time */
	 free( cp );
      }
   }
   else if ((SVC_NOT_GENERIC(sp)) || (!SC_FORKS( SVC_CONF( sp ) ) ) )
     free( cp );
}
Пример #6
0
static void dgram_echo( const struct server *serp )
{
   char            buf[ DATAGRAM_SIZE ] ;
   union xsockaddr lsin;
   ssize_t             cc ;
   socklen_t       sin_len = 0;
   int             descriptor = SERVER_FD( serp ) ;

   if( SC_IPV4( SVC_CONF( SERVER_SERVICE( serp ) ) ) )
      sin_len = sizeof( struct sockaddr_in );
   else if( SC_IPV6( SVC_CONF( SERVER_SERVICE( serp ) ) ) )
      sin_len = sizeof( struct sockaddr_in6 );

   cc = recvfrom( descriptor, buf, sizeof( buf ), 0, (struct sockaddr *)( &lsin ), &sin_len ) ;
   if ( cc != (ssize_t)-1 ) {
      (void) sendto( descriptor, buf, (size_t)cc, 0, SA( &lsin ), sizeof( lsin ) ) ;
   }
}
Пример #7
0
static void dgram_time( const struct server *serp )
{
   char     buf[ 1 ] ;
   unsigned char time_buf[4];
   union xsockaddr lsin ;
   socklen_t       sin_len = 0 ;
   int             fd      = SERVER_FD( serp ) ;
   const char     *func    = "dgram_daytime";

   if ( SC_IPV4( SVC_CONF( SERVER_SERVICE( serp ) ) ) ) 
      sin_len = sizeof( struct sockaddr_in );
   else if ( SC_IPV6( SVC_CONF( SERVER_SERVICE( serp ) ) ) ) 
      sin_len = sizeof( struct sockaddr_in6 );

   if ( recvfrom( fd, buf, sizeof( buf ), 0, SA( &lsin ), &sin_len ) == -1 )
      return ;

   time_protocol( time_buf ) ;
   (void) sendto( fd, (char *) time_buf, 4, 0, SA( &lsin ), sin_len ) ;
}
Пример #8
0
static void dgram_daytime( const struct server *serp )
{
   char            time_buf[ BUFFER_SIZE ] ;
   union xsockaddr lsin ;
   socklen_t       sin_len     = 0 ;
   unsigned int    buflen      = sizeof( time_buf ) ;
   int             descriptor  = SERVER_FD( serp ) ;
   const char     *func       = "dgram_daytime";

   if ( SC_IPV4( SVC_CONF( SERVER_SERVICE( serp ) ) ) ) 
      sin_len = sizeof( struct sockaddr_in );
   else if ( SC_IPV6( SVC_CONF( SERVER_SERVICE( serp ) ) ) ) 
      sin_len = sizeof( struct sockaddr_in6 );

   if ( recvfrom( descriptor, time_buf, sizeof( time_buf ), 0,
            SA( &lsin ), &sin_len ) == -1 )
      return ;

   daytime_protocol( time_buf, &buflen ) ;
   
   (void) sendto( descriptor, time_buf, buflen, 0, SA(&lsin), sizeof( lsin ) ) ;
}
Пример #9
0
static void deactivate( const struct service *sp )
{
   (void) Sclose( SVC_FD( sp ) ) ;

#ifdef HAVE_MDNS
   xinetd_mdns_deregister(SVC_CONF(sp));
#endif

   if (debug.on)
      msg(LOG_DEBUG, "deactivate", "%d Service %s deactivated", 
          getpid(), SC_NAME( SVC_CONF(sp) ) );

#ifndef NO_RPC
   if ( SC_IS_RPC( SVC_CONF( sp ) ) )
   {
      unsigned long vers ;
      const struct rpc_data *rdp = SC_RPCDATA( SVC_CONF( sp ) ) ;

      for ( vers = RD_MINVERS( rdp ) ; vers <= RD_MAXVERS( rdp ) ; vers++ ) {
         (void) pmap_unset( RD_PROGNUM( rdp ), vers ) ;
      }
   }
#endif   /* ! NO_RPC */
}
Пример #10
0
/*
 * Allocate a new struct service and initialize it from scp 
 */
struct service *svc_new( struct service_config *scp )
{
   struct service *sp ;
   const char *func = "svc_new" ;

   sp = NEW_SVC() ;
   if ( sp == NULL )
   {
      out_of_memory( func ) ;
      return( NULL ) ;
   }
   CLEAR( *sp ) ;

   SVC_CONF(sp) = scp ;
   return( sp ) ;
}
Пример #11
0
static int banner_fail( const struct service *sp, const connection_s *cp )
{
   const char *func = "banner_fail";
   const struct service_config *scp = SVC_CONF( sp ) ;


   if ( SC_BANNER_FAIL(scp) != NULL )
   {
      char tmpbuf[TMPSIZE];
      int retval;
      int bannerfd = open(SC_BANNER_FAIL(scp), O_RDONLY);

      if( bannerfd < 0 )
      {
         msg( LOG_ERR, func, "service = %s, open of banner %s failed", 
            SVC_ID( sp ), SC_BANNER_FAIL(scp));
         return(-1);
      }

      while( (retval = read(bannerfd, tmpbuf, sizeof(tmpbuf))) ) {
         if (retval == -1)
         {
            if (errno == EINTR)
               continue;
            else
            {
               msg(LOG_ERR, func, "service %s, Error %m reading banner %s", 
			       SVC_ID( sp ), SC_BANNER(scp));
               break;
            }
         }
         Swrite(cp->co_descriptor, tmpbuf, retval);
      }

      Sclose(bannerfd);
      Sflush ( cp->co_descriptor );
   }

   return(0);
}
Пример #12
0
/*
 * Get a new connection request and initialize 'cp' appropriately
 */
static status_e get_connection( struct service *sp, connection_s *cp )
{
   struct service_config *scp = SVC_CONF( sp );
   socklen_t sin_len;
   const char *func = "get_connection" ;
   int on = 1;

   if( SC_IPV4(scp) ) sin_len = sizeof(struct sockaddr_in);
   if( SC_IPV6(scp) ) sin_len = sizeof(struct sockaddr_in6);

   if ( SVC_SOCKET_TYPE( sp ) == SOCK_STREAM ) {
      /* If it's a TCP socket, and we're set to wait, the accept is
       * done by the child process.  Don't set NEW_DESCRIPTOR, since
       * there isn't one.  The descriptor will be/was removed from
       * the descriptor set in svc_suspend and re-enabled in svc_resume.
       */
      if( SC_WAITS( scp ) ) {
         cp->co_descriptor = SVC_FD( sp );
      } else {
         cp->co_descriptor = accept( SVC_FD( sp ), &(cp->co_remote_address.sa),
                                     &sin_len ) ;
	 if (cp->co_descriptor != -1)
             M_SET( cp->co_flags, COF_NEW_DESCRIPTOR ) ;
      }

      if ( cp->co_descriptor == -1 )
      {
	 if ((errno == EMFILE) || (errno == ENFILE))
	     cps_service_stop(sp, "no available descriptors");
	 else
             msg( LOG_ERR, func, "service %s, accept: %m", SVC_ID( sp ) ) ;
         return( FAILED ) ;
      }

      if( SC_NODELAY( scp ) && (SC_PROTOVAL( scp ) == IPPROTO_TCP) )
         if( setsockopt(SVC_FD(sp), IPPROTO_TCP, TCP_NODELAY, 
                        (char *)&on, sizeof( on ) ) < 0 )
            msg( LOG_WARNING, func, "service %s, setsockopt: %m", SVC_ID(sp));

      if( SC_KEEPALIVE( scp ) && (SC_PROTOVAL( scp ) == IPPROTO_TCP) )
      {
         if( setsockopt(SVC_FD(sp), SOL_SOCKET, SO_KEEPALIVE, 
                        (char *)&on, sizeof( on ) ) < 0 )
            msg( LOG_WARNING, func, "service %s, setsockopt: %m", SVC_ID(sp));
      }
      
      if( SC_IPV6(scp) && !(SC_V6ONLY( scp ))  && 
         (IN6_IS_ADDR_V4MAPPED(&cp->co_remote_address.sa_in6.sin6_addr) || 
	  IN6_IS_ADDR_V4COMPAT(&cp->co_remote_address.sa_in6.sin6_addr)) ) 
      {
         int af = AF_INET;
         if( setsockopt(cp->co_descriptor, IPPROTO_IPV6,
               IPV6_ADDRFORM, &af, sizeof( af ) ) ) {
            if( debug.on ) msg( LOG_WARNING, func, "service %s, IPV6_ADDRFORM setsockopt() failed: %m", SVC_ID( sp) );
         }
      }

      M_SET( cp->co_flags, COF_HAVE_ADDRESS ) ;
   }
   else
   {
      if ( SVC_SOCKET_TYPE( sp ) == SOCK_DGRAM )
      {
         char t_ch ;
	 ssize_t val;

         /*
          * This trick is done to get the remote address.
          * select(2) guaranteed that we won't block on the recvfrom
          */
	 val = recvfrom( SVC_FD( sp ), &t_ch, 1, MSG_PEEK,
                              &cp->co_remote_address.sa, &sin_len );
         if ( val == (ssize_t)-1 )
         {
            msg( LOG_ERR, func, "service %s, recvfrom: %m", SVC_ID( sp ) ) ;
            return( FAILED ) ;
         }
         M_SET( cp->co_flags, COF_HAVE_ADDRESS ) ;
      }

      cp->co_descriptor = SVC_FD( sp ) ;
   }

   return( OK ) ;
}
Пример #13
0
/* This function gets called from child.c after we have been forked */
void redir_handler( struct server *serp )
{
   struct service *sp = SERVER_SERVICE( serp );
   struct service_config *scp = SVC_CONF( sp );
   int RedirDescrip = SERVER_FD( serp );
   int maxfd, num_read, num_wrote=0, ret=0;
   unsigned int sin_len = 0;
   unsigned long bytes_in = 0, bytes_out = 0;
   int no_to_nagle = 1;
   int on = 1, v6on;
   char buff[NET_BUFFER];
   fd_set rdfd, msfd;
   struct timeval *timep = NULL;
   const char *func = "redir_handler";
   union xsockaddr serveraddr ;

   if( signal(SIGPIPE, redir_sigpipe) == SIG_ERR ) 
      msg(LOG_ERR, func, "unable to setup signal handler");

   close_all_svc_descriptors();

   /* If it's a tcp service we are redirecting */
   if( scp->sc_protocol.value == IPPROTO_TCP )
   {
      memcpy(&serveraddr, scp->sc_redir_addr, sizeof(serveraddr));
      if( serveraddr.sa_in.sin_family == AF_INET ) {
         sin_len = sizeof( struct sockaddr_in );
         RedirServerFd = socket(AF_INET, SOCK_STREAM, 0);
       } else if( serveraddr.sa_in.sin_family == AF_INET6 ) {
         sin_len = sizeof( struct sockaddr_in6 );
         RedirServerFd = socket(AF_INET6, SOCK_STREAM, 0);
      } else {
         msg(LOG_ERR, func, "not a valid protocol. Use IPv4 or IPv6.");
         exit(0);
      }

      if( RedirServerFd < 0 )
      {
         msg(LOG_ERR, func, "cannot create socket: %m");
         exit(0);
      }

      if( SC_IPV6( scp ) ) {
         if( SC_V6ONLY( scp ) ) {
            v6on = 1;
         } else {
            v6on = 0;
         }
#ifdef IPV6_V6ONLY
         if( setsockopt(RedirServerFd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&v6on, sizeof(v6on)) < 0 ) { 
            msg( LOG_ERR, func, "Setting IPV6_V6ONLY option failed (%m)" );
         }
#endif

      }
      if( SC_KEEPALIVE( scp ) )
         if (setsockopt(RedirServerFd, SOL_SOCKET, SO_KEEPALIVE, 
                        (char *)&on, sizeof( on ) ) < 0 )
            msg(LOG_ERR, func, 
                "setsockopt SO_KEEPALIVE RedirServerFd failed: %m");
      
      if( serveraddr.sa_in.sin_family == AF_INET )
         serveraddr.sa_in.sin_port = htons(serveraddr.sa_in.sin_port);
      if( serveraddr.sa_in.sin_family == AF_INET6 )
         serveraddr.sa_in6.sin6_port = htons(serveraddr.sa_in6.sin6_port);

      if( connect(RedirServerFd, &serveraddr.sa, sin_len) < 0 )
      {
         msg(LOG_ERR, func, "can't connect to remote host %s: %m",
            xaddrname( &serveraddr ) );
         exit(0);
      }

      /* connection now established */

      if (setsockopt(RedirServerFd, IPPROTO_TCP, TCP_NODELAY, 
         (char *) &no_to_nagle, sizeof( on ) ) < 0) {

         msg(LOG_ERR, func, "setsockopt RedirServerFd failed: %m");
      }

      if (setsockopt(RedirDescrip, IPPROTO_TCP, TCP_NODELAY, 
         (char *) &no_to_nagle, sizeof( on ) ) < 0) {

         msg(LOG_ERR, func, "setsockopt RedirDescrip failed: %m");
      }

      maxfd = (RedirServerFd > RedirDescrip)?RedirServerFd:RedirDescrip;
      FD_ZERO(&msfd);
      FD_SET(RedirDescrip, &msfd);
      FD_SET(RedirServerFd, &msfd);

      while(1) {
         memcpy(&rdfd, &msfd, sizeof(rdfd));
         if (select(maxfd + 1, &rdfd, (fd_set *)0, (fd_set *)0, timep) <= 0) {
            /* place for timeout code, currently does not time out */
            break;
         }

         if (FD_ISSET(RedirDescrip, &rdfd)) {
            do {
               num_read = read(RedirDescrip,
                  buff, sizeof(buff));
               if (num_read == -1 && errno == EINTR)
                  continue;
               if (num_read <= 0)
                  goto REDIROUT;
               bytes_in += num_read;
            } while (num_read < 0);

            /* Loop until we have written everything
             * that was read */
            num_wrote = 0;
            while( num_wrote < num_read ) {
               ret = write(RedirServerFd,
                  buff + num_wrote,
                  num_read - num_wrote);
               if (ret == -1 && errno == EINTR)
                  continue;
               if (ret <= 0)
                  goto REDIROUT;
               num_wrote += ret;
            }
         }

         if (FD_ISSET(RedirServerFd, &rdfd)) {
            do {
               num_read = read(RedirServerFd,
                  buff, sizeof(buff));
               if (num_read == -1 && errno == EINTR)
                  continue;
               if (num_read <= 0)
                  goto REDIROUT;
               bytes_out += num_read;
            } while (num_read < 0);

            /* Loop until we have written everything
             * that was read */
            num_wrote = 0;
            while( num_wrote < num_read ) {
               ret = write(RedirDescrip,
                  buff + num_wrote,
                  num_read - num_wrote);
               if (ret == -1 && errno == EINTR)
                  continue;
               if (ret <= 0)
                  goto REDIROUT;
               num_wrote += ret;
            }
         }
      }
REDIROUT:
      if( M_IS_SET( (scp)->sc_log_on_success, LO_TRAFFIC ) ) {
         svc_logprint( SERVER_CONNSERVICE( serp ), "TRAFFIC",
                       "in=%lu(bytes) out=%lu(bytes)", bytes_in, bytes_out );
      }

      exit(0);
   }

   msg(LOG_ERR, func, 
   "redirect with any protocol other than tcp is not supported at this time.");
   exit(0);
}
Пример #14
0
/*
 * This function is running in the new process
 */
void exec_server( const struct server *serp )
{
   const struct service_config *scp = SVC_CONF( SERVER_SERVICE( serp ) ) ;
   struct rlimit rl ;
   int fd ;
   int descriptor = SERVER_FD( serp ) ;
   const char *server = SC_SERVER( scp ) ;
   const char *func = "exec_server" ;

   /*
    * The following code solves a problem with post-version-4.3
    * Ultrix systems (the bug was reported, and a fix was provided by
    * [email protected]; a slightly modified version of this
    * fix is included here).
    *
    * If this is a 'nowait' service, we pass the service descriptor
    * to the server. Note that we have set the close-on-exec flag
    * on all service descriptors. It is unclear whether the dup2()
    * will create a descriptor with the close-on-exec flag set,
    * so we explicitly clear the flag (since we are doing this
    * after the fork, it does not affect the descriptor of the
    * parent process).
    */
   if ( fcntl( descriptor, F_SETFD, 0 ) == -1 )
      msg( LOG_WARNING, func,
         "fcntl( %d, clear close-on-exec ) failed: %m", descriptor ) ;

   if ( debug.on )
      msg( LOG_DEBUG, func, "duping %d", descriptor ) ;

   for ( fd = 0 ; fd <= MAX_PASS_FD ; fd++ )
   {
      if ( dup2( descriptor, fd ) == -1 )
      {
         msg( LOG_ERR, func,
               "dup2( %d, %d ) failed: %m", descriptor, fd ) ;
         _exit( 1 ) ;
      }
   }


#ifdef RLIMIT_NOFILE
   rl.rlim_max = ps.ros.orig_max_descriptors ;
   rl.rlim_cur = ps.ros.max_descriptors ;
   (void) setrlimit( RLIMIT_NOFILE, &rl ) ;
#endif
#ifdef RLIMIT_AS
   if (SC_RLIM_AS (scp))
   {
      rl.rlim_cur = SC_RLIM_AS( scp );
      rl.rlim_max = SC_RLIM_AS( scp );
      (void) setrlimit( RLIMIT_AS, &rl );
   }
#endif
#ifdef RLIMIT_CPU
   if (SC_RLIM_CPU (scp))
   {
      rl.rlim_cur = SC_RLIM_CPU( scp );
      rl.rlim_max = SC_RLIM_CPU( scp );
      (void) setrlimit( RLIMIT_CPU, &rl );
   }
#endif
#ifdef RLIMIT_DATA
   if (SC_RLIM_DATA (scp))
   {
      rl.rlim_cur = SC_RLIM_DATA( scp );
      rl.rlim_max = SC_RLIM_DATA( scp );
      (void) setrlimit( RLIMIT_DATA, &rl );
   }
#endif
#ifdef RLIMIT_RSS
   if (SC_RLIM_RSS (scp))
   {
      rl.rlim_cur = SC_RLIM_RSS( scp );
      rl.rlim_max = SC_RLIM_RSS( scp );
      (void) setrlimit( RLIMIT_RSS, &rl );
   }
#endif
#ifdef RLIMIT_STACK
   if (SC_RLIM_STACK (scp))
   {
      rl.rlim_cur = SC_RLIM_STACK( scp );
      rl.rlim_max = SC_RLIM_STACK( scp );
      (void) setrlimit( RLIMIT_STACK, &rl );
   }
#endif

   (void) Sclose( descriptor ) ;

#ifndef solaris
   no_control_tty() ;
#endif

   msg_suspend() ;

   (void) execve( server, SC_SERVER_ARGV( scp ),
             env_getvars( SC_ENV( scp )->env_handle ) ) ;

   /*
    * The exec failed. Log the error and exit.
    */
   msg_resume() ;
   msg( LOG_ERR, func, "execv( %s ) failed: %m", server ) ;
   _exit( 0 ) ;
}
Пример #15
0
static void tcpmux_handler( const struct server *serp )
{
   char      svc_name[ BUFFER_SIZE ] ;
   int       cc ;
   int       descriptor = SERVER_FD( serp ) ;
   const     struct service *svc = SERVER_SERVICE( serp ) ;
   unsigned  u;
   struct    service *sp = NULL;
   struct    server server, *nserp;
   struct    service_config *scp = NULL;

   close_all_svc_descriptors();

   /*  Read in the name of the service in the format "svc_name\r\n".
    *
    *  XXX: should loop on partial reads (could probably use Sread() if
    *  it wasn't thrown out of xinetd source code a few revisions back).
    */
   do
   {
      cc = read( descriptor, svc_name, sizeof( svc_name ) ) ;
   } while (cc == -1 && errno == EINTR);

   if ( cc <= 0 )
   {
      msg(LOG_ERR, "tcpmux_handler", "read failed");
      exit(0);
   }

   if ( ( cc <= 2 ) ||
        ( ( svc_name[cc - 1] != '\n' ) || ( svc_name[cc - 2] != '\r' ) ) )
   {
      if ( debug.on )
         msg(LOG_DEBUG, "tcpmux_handler", "Invalid service name format.");
      
      exit(0);
   }

   svc_name[cc - 2] = '\0';  /*  Remove \r\n for compare */

   if ( debug.on )
   {
      msg(LOG_DEBUG, "tcpmux_handler", "Input (%d bytes) %s as service name.",
          cc, svc_name);
   }

   /*  Search the services for the a match on name.
    */

   for ( u = 0 ; u < pset_count( SERVICES( ps ) ) ; u++ )
   {
      sp = SP( pset_pointer( SERVICES( ps ), u ) ) ;

      if ( strcasecmp( svc_name, SC_NAME( SVC_CONF( sp ) ) ) == 0 )
      {
         /*  Found the pointer. Validate its type.
          */
         scp = SVC_CONF( sp );
/*
         if ( ! SVC_IS_MUXCLIENT( sp ) )
         {
            if ( debug.on )
            {
               msg(LOG_DEBUG, "tcpmux_handler", "Non-tcpmux service name: %s.",
                   svc_name);
            }
            exit(0);
         }
*/

         /*  Send the accept string if we're a PLUS (+) client.
          */

         if ( SVC_IS_MUXPLUSCLIENT( sp ) )
         {
            if ( Swrite( descriptor, TCPMUX_ACK, sizeof( TCPMUX_ACK ) ) !=
                 sizeof( TCPMUX_ACK ) )
            {
                msg(LOG_ERR, "tcpmux_handler", "Ack write failed for %s.",
		    svc_name);
                exit(0);
            }
         }
         break;  /*  Time to get on with the service */
      }
      continue;  /*  Keep looking */
   }

   if ( u >= pset_count( SERVICES( ps ) ) )
   {
      if ( debug.on )
      {
         msg(LOG_DEBUG, "tcpmux_handler", "Service name %s not found.",
             svc_name);
      }
      exit(0);
   }

   if( SVC_WAITS( svc ) ) /* Service forks, so close it */
      Sclose(descriptor);

   server.svr_sp = sp;
   server.svr_conn = SERVER_CONNECTION(serp);
   nserp = server_alloc(&server);
   if( SC_IS_INTERNAL( scp ) ) {
      SC_INTERNAL(scp, nserp);
   } else {
      exec_server(nserp);
   }
}
Пример #16
0
/*
 * This function runs in the parent context and updates the global_no_access
 * list. 
 */
void process_sensor( const struct service *sp, const union xsockaddr *addr)
{
   const char *func = "process_sensor";

   if (SC_DENY_TIME(SVC_CONF(sp)) != 0)   /* 0 simply logs it   */
   {
      if ( pset_count( global_no_access ) < MAX_GLOBAL_NO_ACCESS)
      {
         int item_matched = addrlist_match( global_no_access, SA(addr) );

	 if ( item_matched == 0)
	 {   /* no match...adding to the list   */
            char *dup_addr = new_string(xaddrname( addr ) );

	    if (dup_addr == NULL )
               return ;

	    if (addrlist_add(global_no_access, dup_addr) == FAILED)
               msg(LOG_ERR, func,
                  "Failed adding %s to the global_no_access list", dup_addr);
            else
            {
               time_t nowtime;
               char time_buf[40], *tmp;

	       nowtime = time(NULL);
	       msg(LOG_CRIT, func,
	           "Adding %s to the global_no_access list for %d minutes",
	            dup_addr, SC_DENY_TIME(SVC_CONF(sp)));

	       if (SC_DENY_TIME(SVC_CONF(sp)) == -1)
                    strcpy(time_buf, "-1");
               else
                    strx_nprint(time_buf, 38, "%ld",
                       (time_t)nowtime+(60*SC_DENY_TIME(SVC_CONF(sp))));

	       tmp = new_string(time_buf);
               if (tmp != NULL)
               {
                  if (pset_add(global_no_access_time, tmp) == NULL)
                  {
                     msg(LOG_ERR, func,
                         "Failed adding %s to the global_no_access_time list. "
                         "global_no_access list is broken, xinetd needs "
			 "restarting.", dup_addr);
                 /* ideally, we should rollback the previous addr addition.   */
                  }
               }
	       if (pset_count(global_no_access) && (timer_id == 0) )
                  timer_id = xtimer_add( scrub_global_access_list, 60 );
            }
            free(dup_addr);
         }
         else
	 {
	    /* Here again, eh?...update time stamp. */
            char *exp_time;
	    time_t stored_time;

	    item_matched--; /* Is # plus 1, to even get here must be >= 1 */
            exp_time = pset_pointer( global_no_access_time, item_matched ) ;
            if (exp_time == NULL)
               return ;

            if ( parse_base10(exp_time, (int *)&stored_time) )
            {  /* if never let them off, bypass */
               if (stored_time != -1)
               {
                  time_t nowtime, new_time;

                  nowtime = time(NULL);
                  new_time = (time_t)nowtime+(60*SC_DENY_TIME(SVC_CONF(sp)));                     if (difftime(new_time, (time_t)stored_time) > 0.0)
	          {   /* new_time is longer save it   */
		     char time_buf[40], *new_exp_time;

		     strx_nprint(time_buf, 38, "%ld", (long)new_time);
		     new_exp_time = new_string(time_buf);
		     if ( new_exp_time )
		     {
		        free(exp_time);
			global_no_access_time->ptrs[ 
                        (unsigned)item_matched ] = new_exp_time;
                     }
                  }
               }
            }
         }
      }
      else
         msg(LOG_ERR, func, "Maximum global_no_access count reached.");
   }
}
Пример #17
0
static status_e failed_service(struct service *sp, 
                                connection_s *cp, 
                                access_e result)
{
   struct service_config *scp = SVC_CONF( sp ) ;

   if ( result != AC_OK )
   {
      bool_int report_failure = TRUE ;

      /*
       * Try to avoid reporting multiple times a failed attempt to access
       * a datagram-based service from a bad address. We do this because
       * the clients of such services usually send multiple datagrams 
       * before reporting a timeout (we have no way of telling them that
       * their request has been denied).
       */
      if ( result == AC_ADDRESS && SVC_SOCKET_TYPE( sp ) == SOCK_DGRAM )
      {
         if( SC_IPV4( scp ) ) {
            struct sockaddr_in *sinp = SAIN(CONN_ADDRESS( cp )) ;
            struct sockaddr_in *last = SAIN(SVC_LAST_DGRAM_ADDR(sp)) ;
            time_t current_time ;

            if (sinp == NULL )
               return FAILED;

            if ( last == NULL ) {
               last = SAIN( calloc( 1, sizeof(union xsockaddr) ) );
	       SVC_LAST_DGRAM_ADDR(sp) = (union xsockaddr *)last;
            }

            (void) time( &current_time ) ;
            if ( sinp->sin_addr.s_addr == last->sin_addr.s_addr &&
                                          sinp->sin_port == last->sin_port )
            {
               if( current_time - SVC_LAST_DGRAM_TIME(sp) <= DGRAM_IGNORE_TIME )
                  report_failure = FALSE ;
               else
                  SVC_LAST_DGRAM_TIME(sp) = current_time ;
            }
            else
            {
               memcpy(SVC_LAST_DGRAM_ADDR(sp), sinp,sizeof(struct sockaddr_in));
               SVC_LAST_DGRAM_TIME(sp) = current_time ;
            }
         } else if( SC_IPV6( scp ) ) {
            struct sockaddr_in6 *sinp = SAIN6(CONN_ADDRESS( cp )) ;
            struct sockaddr_in6 *last = SAIN6(SVC_LAST_DGRAM_ADDR(sp)) ;
            time_t current_time ;

	    if (sinp == NULL )
               return FAILED;

	    if( last == NULL ) {
               last = SAIN6(calloc( 1, sizeof(union xsockaddr) ) );
	       SVC_LAST_DGRAM_ADDR( sp ) = (union xsockaddr *)last;
            }

            (void) time( &current_time ) ;
            if ( IN6_ARE_ADDR_EQUAL(&(sinp->sin6_addr), &(last->sin6_addr)) && 
                 sinp->sin6_port == last->sin6_port )
            {
               if((current_time - SVC_LAST_DGRAM_TIME(sp)) <= DGRAM_IGNORE_TIME)
                  report_failure = FALSE ;
               else
                  SVC_LAST_DGRAM_TIME(sp) = current_time ;
            }
            else
            {
               memcpy(SVC_LAST_DGRAM_ADDR(sp),sinp,sizeof(struct sockaddr_in6));
               SVC_LAST_DGRAM_TIME(sp) = current_time ;
            }
         }
      }

      if ( report_failure )
         svc_log_failure( sp, cp, result ) ;

      banner_fail(sp, cp);

      return( FAILED ) ;
   }

   return( OK );
}
Пример #18
0
/*
 * This function is invoked in a forked process to run a server. 
 * If the service is internal the appropriate function is invoked
 * otherwise the server program is exec'ed.
 * This function also logs the remote user id if appropriate
 */
void child_process( struct server *serp )
{
   struct service          *sp  = SERVER_SERVICE( serp ) ;
   connection_s            *cp  = SERVER_CONNECTION( serp ) ;
   struct service_config   *scp = SVC_CONF( sp ) ;
   const char              *func = "child_process" ;

   signal_default_state();

   if ((signals_pending[0] >= 0 && Sclose(signals_pending[0])) ||
       (signals_pending[1] >= 0 && Sclose(signals_pending[1])))
   {
      msg(LOG_ERR, func, "Failed to close the signal pipe: %m");
      _exit(1);
   }
   signals_pending[0] = -1;
   signals_pending[1] = -1;

   Sclose(0);
   Sclose(1);
   Sclose(2);


#ifdef DEBUG_SERVER
   if ( debug.on )
   {
      msg( LOG_DEBUG, func, "Process %d is sleeping", getpid() ) ;
      sleep( 10 ) ;
   }
#endif

   if ( ! SC_IS_INTERCEPTED( scp ) )
   {
      set_credentials( scp ) ;
      if ( SC_SPECIFIED( scp, A_NICE ) )
         (void) nice( SC_NICE( scp ) ) ;
   }

   if ( svc_child_access_control(sp, cp) != OK )
      exit(0);

   if ( SERVER_LOGUSER( serp ) )
   {
      unsigned   timeout ;
      idresult_e result ;
      
      /*
       * We use LOGUSER_SUCCESS_TIMEOUT unless the service requires
       * identification, in which case we use an infinite timeout
       */
      timeout = SC_MUST_IDENTIFY( scp ) ? 0 : LOGUSER_SUCCESS_TIMEOUT ;
      result = log_remote_user( serp, timeout ) ;

      if ( result != IDR_OK && SC_MUST_IDENTIFY( scp ) )
      {
         svc_logprint( sp, NOID_ENTRY, "%s %s",
                  conn_addrstr( SERVER_CONNECTION( serp ) ),
                     idresult_explain( result ) ) ;
         _exit( 0 ) ;
      }
   }

#ifdef HAVE_SESSIONCREATE
   if ( scp->sc_sessioncreate == YES ) 
   {
      if ( SessionCreate(0, sessionHasTTY|sessionIsRemote) != noErr )
         svc_logprint( sp, "SessionCreate", "SessionCreate() failed!" );
   }
#endif

   /* this is where the server gets executed  -bbraun */
   if ( ! SC_IS_INTERNAL( scp ) )
   {
      if( scp->sc_redir_addr != NULL )
      {
         redir_handler( serp );
      }
      else
      {
#if defined(HAVE_SETENV)
         char buff[1024];

         strx_sprint(buff, sizeof(buff)-1, "REMOTE_HOST=%s", conn_addrstr(cp));
         if( env_addstr(SC_ENV(scp)->env_handle, buff) != ENV_OK ) {
            msg( LOG_ERR, func, "Error adding REMOTE_HOST variable for %s: %m", SC_NAME(scp) );
            _exit( 1 ) ;
         }
#endif
         exec_server( serp ) ;
      }
   }
   else
   {
      char name[ 180 ] ;
      /*
       * We don't bother to disassociate from the controlling terminal
       *   (we have a controlling terminal only if debug.on is TRUE)
       *
       * Also, for interceptor processes, we give them the name:
       *            <program_name> <service-id> interceptor
       */
      if ( SC_IS_INTERCEPTED( scp ) )
         strx_print( INT_NULL, name, sizeof( name ) - 1,
                           "%s %s interceptor", program_name, SC_ID( scp ) ) ;
      else
      {
         int namelen = sizeof( name ) - 1 ;      /* leave space for the NUL */
         char host[NI_MAXHOST];
         size_t hostlen = NI_MAXHOST;
         socklen_t addrlen = 0;
         union xsockaddr *sinp = CONN_XADDRESS(SERVER_CONNECTION(serp));
         int len;

         if( sinp == NULL )
            exit(0);

         if( SC_IPV6(scp) ) addrlen = sizeof(struct sockaddr_in6);
         else if( SC_IPV4(scp) ) addrlen = sizeof(struct sockaddr_in);

         len = strx_nprint(name, namelen, "(%s service) %s", program_name,
            SC_ID( scp ) ) ;

         if( getnameinfo( SA(sinp), addrlen, host, hostlen, NULL, 0, 0) != 0 )
               strcpy(host, "unknown");

         if ( SC_IPV6(scp) && SC_ACCEPTS_CONNECTIONS( scp ) && 
               !IN6_IS_ADDR_UNSPECIFIED(&sinp->sa_in6.sin6_addr) )
            strx_print( INT_NULL, &name[ len ], namelen - len, " %s" , host ) ;
         if ( SC_IPV4(scp) && SC_ACCEPTS_CONNECTIONS( scp ) )
            strx_print( INT_NULL, &name[ len ], namelen - len, " %s", host ) ;
      }
      rename_process( name ) ;
      SVC_INTERNAL( sp, serp ) ;
   }
   _exit( 0 ) ;
   /* NOTREACHED */
}
Пример #19
0
void svc_free( struct service *sp )
{
   sc_free( SVC_CONF(sp) ) ;
   CLEAR( *sp ) ;
   FREE_SVC( sp ) ;
}
Пример #20
0
static status_e activate_rpc( struct service *sp )
{
   union xsockaddr        tsin;
   socklen_t              sin_len = sizeof(tsin);
   unsigned long          vers ;
   struct service_config *scp = SVC_CONF( sp ) ;
   struct rpc_data       *rdp = SC_RPCDATA( scp ) ;
   char                  *sid = SC_ID( scp ) ;
   unsigned               registered_versions = 0 ;
   int                    sd = SVC_FD( sp ) ;
   const char            *func = "activate_rpc" ;

   if( SC_BIND_ADDR(scp) != 0 )
      memcpy( &tsin, SC_BIND_ADDR(scp), sizeof(tsin) );
   else
      memset( &tsin, 0, sizeof(tsin));

   if ( SC_PROTOVAL ( scp ) == IPPROTO_TCP ) {
      M_SET ( scp->sc_xflags, SF_NOLIBWRAP );
   }
   if( SC_IPV4( scp ) ) {
      tsin.sa_in.sin_family = AF_INET ;
      sin_len = sizeof(struct sockaddr_in);
   } else if( SC_IPV6( scp ) ) {
      tsin.sa_in6.sin6_family = AF_INET6 ;
      sin_len = sizeof(struct sockaddr_in6);
   }

   if ( bind( sd, &tsin.sa, sin_len ) == -1 )
   {
      msg( LOG_ERR, func, "bind failed (%m). service = %s", sid ) ;
      return( FAILED ) ;
   }

   /*
    * Find the port number that was assigned to the socket
    */
   if ( getsockname( sd, &tsin.sa, &sin_len ) == -1 )
   {
      msg( LOG_ERR, func,
            "getsockname failed (%m). service = %s", sid ) ;
      return( FAILED ) ;
   }
   
   if( tsin.sa.sa_family == AF_INET )
      SC_SET_PORT( scp, ntohs( tsin.sa_in.sin_port ) ) ;
   else if( tsin.sa.sa_family == AF_INET6 )
      SC_SET_PORT( scp, ntohs( tsin.sa_in6.sin6_port ) ) ;

   /*
    * Try to register as many versions as possible
    */
   for ( vers = RD_MINVERS( rdp ) ; vers <= RD_MAXVERS( rdp ) ; vers++ ) {
/*      Is this right?  For instance, if we have both tcp and udp services,
 *      this will unregister the previously registered protocol.
 *      pmap_unset(RD_PROGNUM(rdp), vers);
 */
      if ( pmap_set( RD_PROGNUM( rdp ), vers, SC_PROTOVAL( scp ), 
			      SC_PORT( scp ) ) )
         registered_versions++ ;
      else
         msg( LOG_ERR, func,
            "pmap_set failed. service=%s program=%ld version=%ld",
               sid, RD_PROGNUM( rdp ), vers ) ;
      sleep(1);
   }

   if ( debug.on )
      msg( LOG_DEBUG, func,
            "Registered %d versions of %s", registered_versions, sid ) ;

   return( ( registered_versions == 0 ) ? FAILED : OK ) ;
}
Пример #21
0
/*
 * Activate a service. 
 */
status_e svc_activate( struct service *sp )
{
   struct service_config    *scp = SVC_CONF( sp ) ;
   status_e                  status ;
   const char                     *func = "svc_activate" ;

   /*  No activation for MUXCLIENTS.
    */

   if (SC_IS_MUXCLIENT( scp ))
   {
      return( OK );
   }

   if( SC_IPV4( scp ) ) {
      SVC_FD(sp) = socket( AF_INET, 
                           SC_SOCKET_TYPE( scp ), SC_PROTOVAL( scp ) ) ;
   } else if( SC_IPV6( scp ) ) {
      SVC_FD(sp) = socket( AF_INET6, 
                           SC_SOCKET_TYPE( scp ), SC_PROTOVAL( scp ) ) ;
   }

   if ( SVC_FD(sp) == -1 )
   {
      msg( LOG_ERR, func,
                  "socket creation failed (%m). service = %s", SC_ID( scp ) ) ;
      return( FAILED ) ;
   }

   if ( set_fd_modes( sp ) == FAILED )
   {
      (void) Sclose( SVC_FD(sp) ) ;
      return( FAILED ) ;
   }

#ifndef NO_RPC
   if ( SC_IS_RPC( scp ) )
      status = activate_rpc( sp ) ;
   else
#endif   /* ! NO_RPC */
      status = activate_normal( sp ) ;
   
   if ( status == FAILED )
   {
      (void) Sclose( SVC_FD(sp) ) ;
      return( FAILED ) ;
   }

#ifdef HAVE_MDNS
   xinetd_mdns_register(scp);
#endif

   if ( log_start( sp, &SVC_LOG(sp) ) == FAILED )
   {
      deactivate( sp ) ;
      return( FAILED ) ;
   }

   /*
    * Initialize the service data
    */
   SVC_RUNNING_SERVERS(sp)   = SVC_RETRIES(sp) = 0 ;

   if ( SC_MUST_LISTEN( scp ) )
      (void) listen( SVC_FD(sp), LISTEN_BACKLOG ) ;

   ps.rws.descriptors_free-- ;

   SVC_STATE(sp) = SVC_ACTIVE ;

   FD_SET( SVC_FD(sp), &ps.rws.socket_mask ) ;
   if ( SVC_FD(sp) > ps.rws.mask_max )
      ps.rws.mask_max = SVC_FD(sp) ;

   ps.rws.active_services++ ;
   ps.rws.available_services++ ;

   return( OK ) ;
}
Пример #22
0
static status_e activate_normal( struct service *sp )
{
   union xsockaddr         tsin;
   int                     sd             = SVC_FD( sp ) ;
   struct service_config  *scp            = SVC_CONF( sp ) ;
   uint16_t                service_port   = SC_PORT( scp ) ;
   char                   *sid            = SC_ID( scp ) ;
   const char             *func           = "activate_normal" ;
   unsigned int            sin_len        = sizeof(tsin);
   int                     on             = 1;
#ifdef IPV6_V6ONLY
   int                     v6on           = 0;
#endif

   if( SC_BIND_ADDR(scp) != NULL )
      memcpy(&tsin, SC_BIND_ADDR(scp), sin_len);
   else
      memset(&tsin, 0, sin_len);
   
   if( SC_IPV4( scp ) ) {
      tsin.sa_in.sin_family = AF_INET ;
      tsin.sa_in.sin_port = htons( service_port ) ;
      sin_len = sizeof(struct sockaddr_in);
   } else if( SC_IPV6( scp ) ) {
      tsin.sa_in6.sin6_family = AF_INET6;
      tsin.sa_in6.sin6_port = htons( service_port );
      sin_len = sizeof(struct sockaddr_in6);
   }

#ifdef IPV6_V6ONLY
   if( SC_IPV6(scp) ) {
      if( SC_SPECIFIED(scp, A_V6ONLY) ) {
         v6on = 1;
      } else {
         v6on = 0;
      }
      if( setsockopt(sd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&v6on, sizeof(v6on)) < 0 ) {
         msg( LOG_ERR, func, "Setting IPV6_V6ONLY option failed (%m)" );
      }
   }
#endif

   if ( setsockopt( sd, SOL_SOCKET, SO_REUSEADDR, 
                    (char *) &on, sizeof( on ) ) == -1 )
      msg( LOG_WARNING, func, 
           "setsockopt SO_REUSEADDR failed (%m). service = %s", sid ) ;

   if( SC_NODELAY( scp ) && (SC_PROTOVAL(scp) == IPPROTO_TCP) )
   {
      if ( setsockopt( sd, IPPROTO_TCP, TCP_NODELAY, 
                       (char *) &on, sizeof( on ) ) == -1 )
         msg( LOG_WARNING, func, 
              "setsockopt TCP_NODELAY failed (%m). service = %s", sid ) ;
   }

   if( SC_KEEPALIVE( scp ) && (SC_PROTOVAL(scp) == IPPROTO_TCP) ) 
   {
      if( setsockopt(sd, SOL_SOCKET, SO_KEEPALIVE, 
                     (char *)&on, sizeof( on ) ) < 0 )
         msg( LOG_WARNING, func, 
              "setsockopt SO_KEEPALIVE failed (%m). service = %s", sid ) ;
   }

   if ( bind( sd, &tsin.sa, sin_len ) == -1 )
   {
      msg( LOG_ERR, func, "bind failed (%m). service = %s", sid ) ;
      return( FAILED ) ;
   }

#ifdef IN_MULTICAST
   if( SC_IPV4(scp) && IN_MULTICAST( ntohl(tsin.sa_in.sin_addr.s_addr) ) ) {
      struct ifaddrs *addrs, *addr;
      struct ip_mreq mreq;
      
      if (getifaddrs(&addrs) == 0) {
         addr = addrs;

         while (addr)
         {
            if (addr->ifa_addr && (addr->ifa_flags & IFF_MULTICAST))
            {
               mreq.imr_multiaddr.s_addr = tsin.sa_in.sin_addr.s_addr;
               mreq.imr_interface.s_addr = ((struct sockaddr_in *)addr->ifa_addr)->sin_addr.s_addr;
               setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
            }
            addr = addr->ifa_next;
         }
         freeifaddrs(addrs);
         if ( debug.on )
               msg( LOG_DEBUG, func, "Adding multicast membership." );
      }
      else {
         msg( LOG_ERR, func, "getifaddrs failed (%m). service = %s", sid );
      }
   }
#endif

   return( OK ) ;
}