Re: Yet Another Intro

Dana Nowell (DanaNowell@corsof.com)
Tue, 28 Mar 1995 09:49:15 -0500

--=====================_796427355==_
Content-Type: text/plain; charset="us-ascii"

See below, it's pretty simple.

>I'd love to get a copy... Can you please send it to me in text?  (source)
>
>Thanks,
>
>--
>T. Jason Ucker						415.390.3720
>Sys Admin/jr Sys Engineer				tju@sgi.com
>SGI Direct
>

--=====================_796427355==_
Content-Type: text/plain; charset="us-ascii"

/*
*     TcpAlert
*        Copyright (c) 1995 - Dana Nowell
*
*           This code is provided AS-IS, no warranty of any type is granted
*           or implied (including fitness of use), USE AT YOUR OWN RISK.
*
*           Redistribution and use in source forms, with and without
*           modification, are permitted provided that this entire comment
*           appears intact.
*
*           Redistribution in binary form may occur without restriction.
*           Obviously, it would be nice if you gave credit where it is due.
*
*           If you modify this code PLEASE add a comment after this one
*           describing your modifications, we should all leave footprints
*           for the next guy. (See the change log comment)
*
*
*  General design notes:
*     This code ASSUMES AN ANSI COMPILER!  If your compiler is not ANSI,
*     the function prototypes and declarations will give you fits, sorry.
*
*     This is NOT designed to be fast, small, and elegant.  It IS designed
*     to be easy to read, easy to modify, and 'obviously' correct.
*
*     Logging to syslog is accomplished via four functions LogDebug,
*     LogInfo, LogWarn, and LogError.  This allows you a place to make simple
*     changes if you dislike my choices of log levels.  Currently, LogInfo
*     and LogWarn use the same log level, LOG_WARNING.  This is because
*     of some of the machines I use it on, they only log warnings and above.
*     You may want to change LogInfo to use LOG_NOTICE (I probably will in
*     the future).  The LogDebug function is provided for debugging purposes.
*     It uses LOG_DEBUG as the logging level.  These functions were provided
*     as function calls and not macros to allow people to customize at any
*     level, even to the point of not using syslog.
*
*  Architecture:
*     Very simple.  We create a socket, bind to a port and sit on an accept.
*     When the accept completes, we spin off a child to handle it and go
*     back to sitting on an accept.  The child does all the real work in
*     function HandleConnection().  The basic info about the connection
*     is logged to syslog in function LogPartnerInfo().
*
*  Porting Notes:
*     This was coded for use on LINUX and compiled with gcc, if you port
*     it you need to look at the following areas.
*        Includes    - your mileage my vary and your compiler may bitch
*        cleanchild  - specifically the wait (may be wait3 or waitpid)
*                    - signal call to reset signal (may not need it)
*
*     This was coded for AF_INET sockets, if you want AF_ISO, AF_UNIX
*     or AF_CCITT you are on your own.  The best I can say is you will
*     not be using a sockaddr_in structure and at a minimum all references
*     to it (and its elements) will need to be changed.  I carry the size
*     of the socket structure around in socksize just for people like you.
*
*     If you port it to a different platform and want to send me the changes,
*     I will incorporate them in new versions (assuming there are some).
*
*/

/*
*  Change Log:
*
*     03/24/95  Dana Nowell      Initial version
*     03/27/95  Dana Nowell      Ported to HP and SGI
*
*
*/

/*
*  defines for compile time code inclusion
*
*  I put them here because I was too lazy to make a makefile, plus it
*  improves documentation (yeah, sure it does), you do whatever you want
*
*/


      /*
      *  Which type of wait is used in function cleanchild.
      *  check only one PLEASE
      *  note WAIT3 and WAITPID are not implemented yet.
      *  I have not ported to a box that requires them yet.
      */
#define WAIT_IS_WAIT    1
#define WAIT_IS_WAIT3   0
#define WAIT_IS_WAITPID 0

      /*
      *  causes cleanchild to reset the signal handler.  To see if you
      *  need this: set it to zero, run alert and 'tickle it' two or three
      *  times.  Check the log to ensure you actually did 'tickle it'.
      *  Run ps and see if you have any zombies, if so set it to 1 and
      *  try again.  Most System V R4 systems should be 1.
      */
#define SIGNAL_RESET    1

      /*
      *  I have included a few lines of debugging info in the code.
      *  set this to one to log them to syslog via LogDebug
      */
#define DEBUG_ME        0

      /*
      *  Set to 1 to use Bind/Named as the name resolver
      *  Set to 0 to use only the local host file
      *  Why anyone would ever set this to 0 is beyond me, but you can
      */
#define USE_NAMED       1     /* use resolver and not host file for lookup */



#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <signal.h>
#include <syslog.h>

#if USE_NAMED
#include <arpa/nameser.h>
#include <resolv.h>
#endif



   /*
   *  defines, typedefs, and structure defs used by the program
   */
#define DEFAULT_PORT       1099     /* mainly for testing */
#define MAX_NAME           100
#define MAX_LISTENS        5
#define MAX_SYSLOG_TEXT    200
#define LOGBUF_SIZE        100

   /* various defines for exit states */
#define  EXIT_SUCCESS            0
#define  EXIT_FAILED_UNBLOCK     1
#define  EXIT_FAILED_SOCKET      2
#define  EXIT_FAILED_CMDLINE     3
#define  EXIT_FAILED_INIT        4
#define  EXIT_NO_WAY            99

   /*
   *
   *  I still do not like the definition of MAX_SYSLOG_BUFFER,
   *  the magic 100 is annoying but it allows for the PID, port number,
   *  NULL terminator, and the spacing in the sprintf format string in
   *  function LogIt().  If you mess with the format string, be sure the
   *  buffer is still big enough (I have been liberal in the allocation)!
   *
   */
#define MAX_SYSLOG_BUFFER  (MAX_NAME + 100 + MAX_SYSLOG_TEXT)



   /* ANSI function prototypes */
void  LogPartnerInfo( struct sockaddr_in *HostAddr );
int   GetConnection( int size );
int   OpenMySocket( void );
char  *GetPartnerName( struct sockaddr_in *sap );
void  cleanchild( int s );
void  LogDebug( char *p );
void  LogInfo( char *p );
void  LogWarn( char *p );
void  LogError( char *p );
void  LogIt( int level, char *p );
int   HandleConnection( int *NewSock );
int   HandleAccept( int NewSock );
int   UnblockTerminal( void );
int   ParseCommandLine( int argc, char *argv[] );
int   PostListen( int socket );




      /* data elements */
      /* this has more globals than I would like, but then it is a quick hack */
char copyright[] = "Sections of this program are copyright 1995 by Dana Nowell";
char version[] = "Version 1.0";
int                  MySocket = -1;          /* fd describing socket */
struct sockaddr_in   SockAddr;               /* My side of the connection */
struct sockaddr_in   RealHostAddr;           /* His side of the connection */
char                 msgbuf[LOGBUF_SIZE+1];  /* buffer to format log messages */
int                  MyPid;
int                  MyPort = DEFAULT_PORT;
char                 MyName[MAX_NAME+1];


/***********************************************************************
*
*  Main - this is where it all starts
*
***********************************************************************/
int main( int argc, char *argv[] )
{
int   NewSock;
int   socksize;

printf( "%s, %s - Port Alarm Application\n%s\n\n",
                      argv[0], version, copyright );

   /* get PID for use in error messages */
MyPid = getpid();

/*
*  Handle command line junk,
*  copy to internal variables to avoid later buffer overrun,
*  after the copy we KNOW it will fit because we fixed the max length
*/
if ( ParseCommandLine( argc, argv ) )
   {
   puts( "Failed to parse the command line properly" );
   exit( EXIT_FAILED_CMDLINE );
   }

#if DEBUG_ME
LogDebug( "In main after ParseCmdLine" );
#endif

   /* spin off a child and die to unblock the terminal/shell */
if ( UnblockTerminal() )
   {
   exit(EXIT_FAILED_UNBLOCK);  /* failed to unblock terminal */
   }

   /* initialize any global data structures that need it */
if ( InitDataStructs() )
   exit(EXIT_FAILED_INIT);

   /* write the startup message */
LogInfo( "Started Alert" );

   /* setup child signal handler to avoid zombies */
(void) signal(SIGCHLD, cleanchild);

/*
**  Try to actually open the socket
*/

   /* get a socket for the connection */
socksize = OpenMySocket();
if ( socksize < 0 )  /* if can not get socket, die */
   exit(EXIT_FAILED_SOCKET);

   /*
   *  loop forever, spinning off children to handle each connection
   */
while ( 1 )
   {
   NewSock = GetConnection( socksize );
   if ( NewSock < 0 )
      continue;

   if ( HandleAccept( NewSock ) )
      LogError( "Unable to handle the accept" );


   (void) close( NewSock );         /* close the 'connection' socket */
   NewSock = -1;

   }  /* back to the forever while loop */

return EXIT_NO_WAY;  /* just to make the compiler happy, not used */
}



/***********************************************************************
*
*  InitDataStructs - return 0 on success 1 on failure
*
***********************************************************************/
int InitDataStructs( void )
{
int retval = 0;

   /* initialize the socket structure */
if (SockAddr.sin_family == 0)
        SockAddr.sin_family = AF_INET;

if (SockAddr.sin_addr.s_addr == 0)
        SockAddr.sin_addr.s_addr = INADDR_ANY;

if ( SockAddr.sin_port == 0 )
   SockAddr.sin_port = htons(MyPort);

#if 0
RealHostAddr.sin_family = AF_INET;
RealHostAddr.sin_addr.s_addr = INADDR_ANY;
#endif

return retval;
}



/***********************************************************************
*
*  ParseCommandLine - return 0 on success 1 on failure
*
***********************************************************************/
int ParseCommandLine( int argc, char *argv[] )
{
char  *p;
int   temp;
int   retval = 0;

      /* save off my name, size adjusted, for logging to syslogd */
p = argv[0];
temp = strlen( p );
if ( temp > MAX_NAME )
   p[MAX_NAME] = '\0';

strcpy( MyName, p );

      /* get port number if specified */
if ( argc > 1 )
   MyPort = atoi( argv[1] );

return retval;
}



/***********************************************************************
*
*  UnblockTerminal - return 0 on success 1 on failure
*
***********************************************************************/
int UnblockTerminal()
{
int   pid;

/*
*   spin off a child and die so we unblock the terminal
*/
pid = fork();
if ( pid < 0 )
   {
   LogError( "Can not create child" );
   return 1;
   }

if ( pid != 0 )      /* if parent, die to unblock term, child continues on */
   {
   exit(EXIT_SUCCESS);
   }

   /* now a new process (child process) so reset PID  */
MyPid = getpid();

return 0;
}




/***********************************************************************
*
*  HandleAccept - where the accept is processed
*     returns 0 on success and 1 on failure
*
***********************************************************************/
int HandleAccept( int NewSock )
{
int retval = 0;
int pid;

/*
**  Create a subprocess to process the request.
*/

#if DEBUG_ME
sprintf( msgbuf, "Forking (fd = %d)\n", NewSock );
LogDebug( msgbuf );
#endif

pid = fork();

if (pid == 0)                 /* Child process, log info and die */
   {
   if ( HandleConnection( &NewSock ) )
      LogError( "Failed to handle this connection completely" );

   exit(EXIT_SUCCESS);        /* child has done its job, die */
   }


if (pid < 0)   /*  fork failed */
   {
   LogError( "Cannot fork" );
   sleep(10);
   retval = 1;
   }

return retval;
}




/***********************************************************************
*
*  HandleConnection - where the connection is actually processed
*     returns 0 on success and 1 on failure
*
***********************************************************************/
int HandleConnection( int *NewSock )
{
int retval = 0;

      /* clean up stuff left over from the parent */
(void) close( MySocket );        /* close to old 'listen' socket*/
MySocket = -1;
(void) signal(SIGCHLD, SIG_DFL); /* reset the signal handler */

      /* process the connection here */
LogPartnerInfo( &RealHostAddr ); /* log info about the connection */

      /* add other things here later (IDENTD, FINGER, etc.) */
      /*
      *  NOTE, beware 'recursion wars'.  If you include finger here
      *  and then run this on your finger socket while the
      *  'other site' has done the same, it can get REAL interesting.
      */


      /* clean up the new connect as we are done */
(void) close( *NewSock );         /* cleanup the 'connection' socket */
*NewSock = -1;

return retval;
}




/***********************************************************************
*
*  LogPartnerInfo - logs info about HostAddr to syslog
*
***********************************************************************/
void LogPartnerInfo( struct sockaddr_in *HostAddr )
{

      /* log the  name of the host connecting to us */
sprintf( msgbuf, "Connected to %s", GetPartnerName( HostAddr ) );
LogWarn( msgbuf );

return;
}




/***********************************************************************
*
*  GetConnection - Posts accept and waits for connection
*     returns new socket for the connection
*
***********************************************************************/
int GetConnection( int socksize )
{
int   NewSock;
int   size;

while( 1 )     /* forever is a long time */
   {
   do
      {
      errno = 0;
      size = socksize;
      NewSock = accept( MySocket, (struct sockaddr *) &RealHostAddr, &size );
      } while ( NewSock < 0 && errno == EINTR );

   if ( NewSock >= 0 )     /* got a live one */
      break;

   sprintf( msgbuf, "Accept failure, %d", errno );
   LogError( msgbuf );
   sleep(10);
   }

return NewSock;
}



/***********************************************************************
*
*  OpenMySocket
*        Opens socket, does bind and posts listen
*        returns size of socket on success and -1 on failure
*
***********************************************************************/
int OpenMySocket()
{
int on = 1;
int socksize;

socksize = sizeof(SockAddr);

if ( MySocket < 0 )     /* already open ??? */
   {
      /* get the socket */
   MySocket = socket(SockAddr.sin_family, SOCK_STREAM, 0);
   if (MySocket < 0)
      {        /* probably something already running ??? */
      sprintf( msgbuf, "Cannot create socket, errno %d", errno );
      LogError( msgbuf );
      return -1;
      }

   (void) setsockopt( MySocket, SOL_SOCKET,
                     SO_REUSEADDR, (char *)&on, sizeof(on) );

   (void) setsockopt( MySocket, SOL_SOCKET,
                     SO_KEEPALIVE, (char *)&on, sizeof(on) );

      /* bind to the socket */
   if ( bind( MySocket, (struct sockaddr *) &SockAddr, socksize ) < 0 )
      {
      sprintf( msgbuf, "Cannot bind, errno %d", errno );
      LogError( msgbuf );
      socksize = -1;
      }
   }

if ( PostListen( MySocket ) )
   socksize = -1;

      /* if error shut it down */
if ( socksize < 0 && MySocket >= 0 )
   {
   sprintf( msgbuf, "Error on socket %d, shutting it down", MySocket );
   LogError( msgbuf );
   (void) close( MySocket );
   MySocket = -1;
   }

return socksize;
}




/***********************************************************************
*
*  PostListen - post a listen on socket
*     returns 0 on success or 1 on failure
*
***********************************************************************/
int PostListen( int socket )
{
int retval = 0;

if ( listen( socket, MAX_LISTENS ) < 0 )
   {
   sprintf( msgbuf, "Cannot listen, errno %d", errno );
   LogError( msgbuf );
   retval = 1;
   }

return retval;
}



/***********************************************************************
*
* GetPartnerName -
*      returns pointer to buffer containing 'name' of sap
*
***********************************************************************/
char *GetPartnerName( struct sockaddr_in *sap )
{
static char       NameBuf[ (3*MAX_NAME) ];  /* bigger than actually needed */
struct hostent    *hostinfo;
char              *retval;
char              *quad = NULL;
char              temp[MAX_NAME+1];
int               size;
int               family;

NameBuf[0] = '\0';      /* safety first, no old data returned by mistake */

family = sap->sin_family;
switch ( family )
   {
   case AF_INET:
      quad = inet_ntoa( sap->sin_addr );
      hostinfo = gethostbyaddr((char *) &sap->sin_addr,
                sizeof(sap->sin_addr), AF_INET);
      break;

   default:
      hostinfo = NULL;
      strcpy( NameBuf, "Unknown socket family" );

      sprintf( msgbuf, "In GetPartnerName, bad family %d (%xH)",
                           family, family );
      LogError( msgbuf );

      break;
   }

   /* if we have info, format a proper reply */
if ( hostinfo != NULL )
   {
         /* make it fit, buffer overflow is a BAD thing */
   size = strlen( hostinfo->h_name );
   if ( size > MAX_NAME )
      {
      memcpy( temp, hostinfo->h_name, MAX_NAME );
      temp[ MAX_NAME ] = '\0';
      strcpy( NameBuf, temp );
      }
   else
      strcpy( NameBuf, hostinfo->h_name );
   }


   /* if we have a quad format ID add it */
if ( quad != NULL )
   {
         /* make it fit, buffer overflow is a BAD thing */
   size = strlen( quad );
   if ( size > MAX_NAME )
      quad[ MAX_NAME ] = '\0';

   strcat( NameBuf, " " );
   strcat( NameBuf, quad );
   }

return NameBuf;
}




/***********************************************************************
*
*  cleanchild - called by sigchild signal handler to cleanup children
*
***********************************************************************/
void cleanchild( int s )
{
int status;

#if DEBUG_ME
LogDebug( "In cleanchild, just starting" );
#endif


#if WAIT_IS_WAIT
while( wait( &status ) > 0 )     /* catch sig child to avoid zombies */
   {
   sleep(3);
   }
#endif

#if WAIT_IS_WAIT3
#endif

#if WAIT_IS_WAITPID
#endif

#if DEBUG_ME
LogDebug( "In cleanchild, all done" );
#endif


#ifdef SIGNAL_RESET
(void) signal(SIGCHLD, cleanchild); /* reset signal */
#endif

}



/***********************************************************************
*
*  LogDebug - writes text p to syslogd at DEBUG level
*
***********************************************************************/
void LogDebug( char *p )
{

LogIt( LOG_DEBUG, p );
}



/***********************************************************************
*
*  LogInfo - writes text p to syslogd at WARNING level
*
***********************************************************************/
void LogInfo( char *p )
{

LogIt( LOG_WARNING, p );
}



/***********************************************************************
*
*  LogWarn - writes text p to syslogd at WARNING level
*
***********************************************************************/
void LogWarn( char *p )
{

LogIt( LOG_WARNING, p );
}





/***********************************************************************
*
*  LogError - writes text p to syslogd at ERROR level
*
***********************************************************************/
void LogError( char *p )
{
LogIt( LOG_ERR, p );
}




/***********************************************************************
*
*  LogIt - writes text p to syslogd at the specified error level
*
***********************************************************************/
void LogIt( int level, char *p )
{
static char text[MAX_SYSLOG_BUFFER+1];
int size;

         /* make it fit, buffer overflow is a BAD thing */
size = strlen(p);
if ( size > MAX_SYSLOG_TEXT )
   p[MAX_SYSLOG_TEXT] = '\0';

         /* format the actual syslog message and log it */
      /*
      *  WARNING!  WARNING!  WARNING!  WARNING!  WARNING!  WARNING!
      *
      *  if you modify this format string, see the define for
      *  MAX_SYSLOG_BUFFER.
      */
sprintf( text, "%s [%d] (port %d) - %s", MyName, MyPid, MyPort, p );
syslog( level, text );
}


--=====================_796427355==_--