#include "headers.h" /* includes all important stuff */

/* this binds and listens for a client..  */
void initconn()
{
   curPid = 0;

   if (mainfd > 0)
   {
      close(mainfd);
      mainfd = 0;
   }

   mainfd = socket(AF_INET, SOCK_STREAM, 0);
   if (mainfd == ERROR)
   {
      error("error with socket(): %s\n\n", strerror(errno));
      quit(ERROR);
   }

   setsockopts();
   
   laddr.sin_family = AF_INET;
   laddr.sin_port = htons(locPort); 
   laddr.sin_addr.s_addr = INADDR_ANY;
   memset(&laddr.sin_zero, 0, sizeof(laddr.sin_zero)); 

   while(1)
   {
      if (bind(mainfd, (struct sockaddr *) &laddr, 
               sizeof(struct sockaddr)) == ERROR)
      {
         if (errno == EINTR) continue;
         error("error with bind(): %s\n\n", strerror(errno));

         (void)sleep(MAXPAUSE * 3);
      } 

      else break;
   }

   if (listen(mainfd, MAXCONNS) == ERROR) 
   {
      error("error with listen(): %s\n\n", strerror(errno));
      quit(ERROR);
   }
}


/* ----------------------- */


/* get the connection, process it, etc. (this most of the work) */
int getconn()
{
   register int i;

   int res;
   int ssize; 

   int hnamelen = 0;

   struct hostent *hent = NULL;  /* client's host entry */

/* 
   If we ever implement it to malloc() client space dynamically 
   (rather than all the necessary mem for the clients at once),
   we would need to include this code.  For now, we don't need it

   clients[++curClient] = (struct client *)malloc(sizeof(struct client *));

   if (clients[curClient] == NULL)
   {
      error("error with malloc(): %s\n\n", strerror(errno));
      quit(ERROR);
   }
*/

   curClient = ERROR;

   for (i = 0; i < MAXCONNS; i++)
   {
      if ((clients[i]).free == 1)
      {
         curClient = i, curPid = 0;

         if (debugging == 1) 
         {
            (void)putchar('\n');
            (void)write(dblogfd, "\n", 1);
         }

         debug("next client will get clients[%d]\n", i);

         memset(&clients[i], 0, sizeof(struct client));  
         clients[i].free = ERROR; 
         clients[i].sockfd = ERROR;
         clients[i].numSubIDs = 0;

         break;
      }
   }

   if (curClient == ERROR)
   {
      if (nofree != 1)
      {
         nofree = 1;
         error("couldn't find a free structure.. restarting\n\n");
      }

      return ERROR;
   }

   memset(&clients[curClient].saddr, 0, sizeof(clients[curClient].saddr));
   ssize = sizeof(struct sockaddr_in);

   debug("now waiting for a new client to connect..\n\n");

   while(1)
   {
      clients[curClient].sockfd = 
   		accept(mainfd, (struct sockaddr *) &(clients[curClient]).saddr, 
                       &ssize);

      if ((clients[curClient]).sockfd == ERROR)
      {
         if (errno == EINTR) continue;

         (void)fputc('\n', stderr);
         error("error with accept(): %s\n\n", strerror(errno));

         /* static - dont need this.
         free(clients[curClient--]); */

         memset((char *) &clients[curClient], 0, sizeof(struct client));
         clients[curClient].free = 1;

         return ERROR;
      }

      else break;
   }

   /* initiate the SSL parts */
#  ifndef NOSSL
   res = seteuid(0);
   if (res == ERROR)
   {
      error("error with seteuid: %s\n\n", res);
      quit(ERROR);
   }

   makeSSLconn(clients[curClient].sockfd);

   res = seteuid(pwd->pw_uid);
   if (res == ERROR)
   {
      error("error with seteuid: %s\n\n", res);
      quit(ERROR);
   }
#  endif

   errno = 0;
   hent = gethostbyaddr((char *) &(clients[curClient]).saddr.sin_addr, 
             sizeof(struct in_addr), (clients[curClient]).saddr.sin_family);

   if (hent == NULL)
   {
      if (h_errno == HOST_NOT_FOUND)
         error("error with gethostbyaddr: host not found\n\n");
    
      else if (h_errno == TRY_AGAIN)
         error("error with gethostbyaddr: non-auth'd host/server failure\n\n");

      else if (h_errno == NO_RECOVERY)
         error("error with gethostbyaddr: non-recoverable error\n\n");
       
      else if (h_errno == NO_DATA)
         error("error with gethostbyaddr: no data request for that type\n\n");

      else if (h_errno == NO_ADDRESS)
         error("error with gethostbyaddr: no address\n\n");
 
      if (debugging == 1) 
      {
         (void)putchar('\n');
         (void)write(dblogfd, "\n", 1);
      }
   }

   if (hent && hent->h_name)
   {
      (void)printf("%connection from %s (%s)...\n\n", 
                   (debugging != 1 ? 'C' : 'c'), hent->h_name,
                   (char *)inet_ntoa((clients[curClient]).saddr.sin_addr));

      hnamelen = strlen(hent->h_name) + 1;

      clients[curClient].hname = (char *)malloc(hnamelen+1);
      if (clients[curClient].hname == NULL)
      {
         error("error malloc()'ing hostname\n\n");
         quit(ERROR);
      }

      memset(clients[curClient].hname, 0, hnamelen+1);
      strcpy(clients[curClient].hname, hent->h_name);
   }

   else
   {
      (void)printf("%connection from %s...\n\n", 
                   (debugging != 1 ? 'C' : 'c'),
                   (char *)inet_ntoa((clients[curClient]).saddr.sin_addr));

      hnamelen = strlen(inet_ntoa(clients[curClient].saddr.sin_addr)) + 1;

      clients[curClient].hname = (char *)malloc(hnamelen+1);
      if (clients[curClient].hname == NULL)
      {
         error("error malloc()'ing hostname\n\n");
         quit(ERROR);
      }

      memset(clients[curClient].hname, 0, hnamelen+1);
      (void)strcpy(clients[curClient].hname, 
                   inet_ntoa((clients[curClient]).saddr.sin_addr));
   }

   if (hnamelen-1 > 0)
   {
      char lochname[hnamelen+1];
      memset(lochname, 0, sizeof(lochname));

      res = gethostname(lochname, sizeof(lochname)-1);
      if (res == ERROR)
      {
         signal(SIGPIPE, SIG_IGN);

         error("error with gethostname(): %s\n\n", strerror(errno));

         if (debugging == 1) 
         {
           (void)putchar('\n');
            (void)write(dblogfd, "\n", 1);
         }

         send_data(clients[curClient].sockfd, "ERROR: %s\n", INTERROR);

         close(clients[curClient].sockfd);
         clients[curClient].free = 1;
 
         return ERROR;
      }

/*
      We're going to allow connections from the localhost in case we
      should ever need to (though to emulate the customer's situation,
      it's better to use different hosts.

      if ((strncmp(clients[curClient].hname, "127.0.0.1", 9) == 0) || 
          (strncmp(clients[curClient].hname, "localhost", 9) == 0) ||
          (strncmp(clients[curClient].hname, lochname, 
                   strlen(lochname) == 0) ||
          (strncmp(clients[curClient].hname, 
                   inet_ntoa(laddr.sin_addr), strlen) == 0))
      {
         signal(SIGPIPE, SIG_IGN);

         error("warning: received a connection from the localhost\n\n");
         send_data(clients[curClient].sockfd, "ERROR: %s\n", NOLOCERROR);

         close(clients[curClient].sockfd);
         clients[curClient].free = 1;

         return ERROR;
      }
*/
   }

   else
   {
      error("(in getconn) hostname length < 0, killing client\n\n");
      return ERROR;
   }

   /* ----------------- */

   if ((ntohs(clients[curClient].saddr.sin_port) < 512) ||
       (ntohs(clients[curClient].saddr.sin_port) > IPPORT_RESERVED-1))
   {
      send_data(clients[curClient].sockfd, "ERROR: %s\n", PORTERROR);
      return ERROR;
   }

   else send_data(clients[curClient].sockfd, "SUCCESSFUL connect\n");

   return 0;
}


/* -------------------------------- */


/* set socket options */
void setsockopts()
{
   int res;
   int val = 1;

   /* probe remote host occasionally to make sure we're still connected */
   /* ----------------------------------------------------------------- */


#  ifdef SO_KEEPALIVE
   /* val = 1; */
   res = setsockopt(mainfd, SOL_SOCKET, SO_KEEPALIVE, 
                    (char *)&val, sizeof(val));

   if (res == ERROR)
   {
      error("error with setsockopt (SO_KEEPALIVE): %s\n\n", strerror(errno));
      quit(ERROR);
   }

#  ifdef TCP_KEEPALIVE
   val = PROBETIME; /* PROBETIME */

   res = setsockopt(mainfd, IPPROTO_TCP, TCP_KEEPALIVE, 
                    (char *)&val, sizeof(val));

   if (res == ERROR)
   {
      error("error with setsockopt (TCP_KEEPALIVE): %s\n\n", strerror(errno));
      quit(ERROR);
   }

#  endif
#  endif

   /* SO_KEEPALIVE must be set first */


   /* allow us to reuse the same port with bind */
   /* ----------------------------------------- */

   val = 1; /* enable it */

#  ifdef SO_REUSEADDR
   res = setsockopt(mainfd, SOL_SOCKET, SO_REUSEADDR, 
                    (char *)&val, sizeof(val));

   if (res == ERROR)
   {
      error("error with setsockopt (SO_REUSEADDR): %s\n\n", strerror(errno));
      quit(ERROR);
   }
#  endif
}

