/* This has the socket code, [dis]connecting server code, etc. */
/* ----------------------------------------------------------- */

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

/* called at first of main() to connect to info server */
void initConn(char *primserv, char *secserv)
{
#  if !defined(SUN) && !defined(BSD)
   int res;
#  endif

   unsigned long inaddr;
   char readbuf[MAXREADSIZE];

   struct hostent *hent = NULL;

   prim = 1;

   if (silent != 1) debug("now attempting to connect to an info server...\n");
   
Connect:
   errno = 0, connected = 0;

   memset(readbuf, 0, sizeof(readbuf));
   memset(&daddr.sin_zero, 0, sizeof(daddr.sin_zero));

   inaddr = (unsigned long int)inet_addr(((prim) ? primserv : secserv));

   if (inaddr != INADDR_NONE)
      memcpy(&daddr.sin_addr, &inaddr, sizeof(struct in_addr));   

   else
   {
      hent = gethostbyname(((prim == 1) ? primserv : secserv));

      if (hent == NULL)
      {
         if (silent != 1)
         {
            if (h_errno == HOST_NOT_FOUND)
               error("error with gethostbyname: host not found\n");

            else if (h_errno == TRY_AGAIN)
               error("error with gethostbyname: "
                     "non-auth'd host/server failure\n");

            else if (h_errno == NO_RECOVERY)
               error("error with gethostbyname: non-recoverable error\n");

            else if (h_errno == NO_DATA)
               error("error with gethostbyname: no data request for this\n");

            else if (h_errno == NO_ADDRESS)
               error("error with gethostbyname: no address\n");

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

            (void)write(errlogfd, "\n", 1);
         }

         if (prim == 1)
         {
            prim = 0; /* use secondary server */
            goto Connect;
         }

         else /* they might not have a DNS capabilities.. bad. */
         {
            error("both primary/secondary dns resolutions failed.."
                  "aborting\n\n");

            quit(ERROR);
         }
      }

      memcpy((char *)&daddr.sin_addr, hent->h_addr, hent->h_length);
   }

   if (sockfd > 0)
   {
     (void)close(sockfd);
     sockfd = 0;
   }

   sockfd = socket(AF_INET, SOCK_STREAM, 0);
   if ((sockfd == ERROR) && (errno > 0))
   {
      if (spooling == 1) 
         error("(in spool parent) error with socket(): %s\n\n",
               strerror(errno));

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

     quit(ERROR);
   }

   setsockopts(sockfd, 1);

   if (locPort <= 0) locPort = LOCPORT;

   laddr.sin_addr.s_addr = INADDR_ANY;
   laddr.sin_family = daddr.sin_family = AF_INET;
   laddr.sin_port = htons(locPort), daddr.sin_port = htons(PORT);
   memset(&(laddr.sin_zero), 0, 8), memset(&(daddr.sin_zero), 0, 8);

# if !defined(SUN) && !defined(BSD)
#  ifdef _POSIX_SAVED_IDS
   res = setuid(0);
#  else
   res = seteuid(0);
#  endif

   if (res == ERROR)
   {
      error("error setting [e]uid: %s\n\n", strerror(errno));
      quit(ERROR);
   }
# endif

   rbindport();

# if !defined(SUN) && !defined(BSD)
   if (pwd != NULL)
   {
#     ifdef _POSIX_SAVED_IDS
      res = setuid(pwd->pw_uid);
#     else
      res = seteuid(pwd->pw_uid);
#     endif

      if (res == ERROR)
      {
         error("error setting [e]uid: %s\n\n", strerror(errno));
         quit(ERROR);
      }
   }
# endif

   if (hent && hent->h_name)  
   {
      if (silent != 1)
      {
         if (debugging == 1) 
         {
            (void)putchar('\n');
            (void)write(dblogfd, "\n", 1);
         }

         (void)printf("%connecting to %s (%s) [%s info server]...\n",
                      (debugging != 1 ? 'C' : 'c'),
                      (prim == 1 ? primserv : secserv), 
                      (char *)inet_ntoa(daddr.sin_addr),
                      (prim == 1 ? "primary" : "secondary"));
      }
   }

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

         (void)printf("%connecting to %s [%s info server]...\n",
                      (debugging != 1 ? 'C' : 'c'), 
                      (char *)inet_ntoa(daddr.sin_addr),
                      (prim == 1 ? "primary" : "secondary"));
      }
   }

   while(1)
   {
      errno = 0, connected = 0;

      if (connect(sockfd, (struct sockaddr *)&daddr,
                  sizeof(struct sockaddr)) == ERROR)
      {
         if (errno == EINTR) continue;

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

            error("error with connect(): %s\n"
                  "unable to connect to the %s server\n\n", strerror(errno),
                  (prim == 1 ? "primary" : "secondary"));
         }
   
         if (prim)
         {
            if (silent != 1) 
               error("now connecting to secondary server..\n\n");

            prim = 0; /* use secondary server */
            goto Connect;
         }

         else 
         {
            /* couldn't connect to either server.. start spooling */
            if (silent != 1) debug("had some errors with both servers..\n");
            errors = 1; 

            return;
         }
      }

      else
      {
         debug("connect() succeeded...\n\n");
         break;
      }
   }

#ifndef NOSSL
# if !defined(SUN) && !defined(BSD)
#  ifdef _POSIX_SAVED_IDS
   res = setuid(0);
#  else
   res = seteuid(0);
#  endif

   if (res == ERROR)
   {
      error("error setting [e]uid: %s\n\n", strerror(errno));
      quit(ERROR);
   }
# endif
#endif

   makeSSLconn();

# if !defined(SUN) && !defined(BSD)
   if (pwd != NULL)
   {
#     ifdef _POSIX_SAVED_IDS
      res = setuid(pwd->pw_uid);
#     else
      res = seteuid(pwd->pw_uid);
#     endif

      if (res == ERROR)
      {
         error("error setting [e]uid: %s\n\n", strerror(errno));
         quit(ERROR);
      }
   }
# endif

   recv_data(readbuf, sizeof(readbuf));
   if (silent != 1)
   {
      if (debugging == 1)
      {
         (void)putchar('\n');
         (void)write(dblogfd, "\n", 1);
      }

      debug("checking for successful connect\n");
      debug("(the data is): %s%c", readbuf, 
            (strchr(readbuf, '\n') == NULL ? '\n' : '\0'));
   }

   if (strncmp(readbuf, "SUCCESSFUL connect", 18) == 0)
   {
      connected = 1;

      if (silent == 1)
      {
         silent = 0;

         if (spooling == 1)
            (void)printf("(in spool parent) "
                         "now connected to an info server\n");
      }

      else 
         (void)printf("%cow connected\n\n", (debugging == 1 ? 'n' : 'N'));

      return;
   }

   else
   {
      connected = 0;   
      error("ERROR connecting to server\n"
            "error message: %s\n%c", readbuf, 
            (strchr(readbuf, '\n') == NULL ? '\n' : '\0'));

      if (prim == 1)
      {
         prim = 0;
         goto Connect;
      }

      else
      {
         if (silent != 1) debug("had some errors with both servers..\n");

         errors = 1;
         return;
      }
   }
}
      
      
/* --------------------- */
      
      
/* connect to a server in the list.. not an info server */
int connServ(char *host)
{     
#  if !defined(BSD) && !defined(SUN)
   int res;
#  endif

   char readbuf[MAXREADSIZE];
   struct hostent *hent = NULL;

   memset(readbuf, 0, sizeof(readbuf));
   
   hent = gethostbyname(host);
   if ((hent == NULL) && (silent != 1))
   {
      if (h_errno == HOST_NOT_FOUND)
         error("error with gethostbyname: host not found\n\n");

      else if (h_errno == TRY_AGAIN)
         error("error with gethostbyname: "
               "non-auth'd host/server failure\n\n");

      else if (h_errno == NO_RECOVERY)
         error("error with gethostbyname: non-recoverable error\n\n");

      else if (h_errno == NO_DATA)
         error("error with gethostbyname: no data request for this\n\n");

      else if (h_errno == NO_ADDRESS)
         error("error with gethostbyname: no address\n\n");

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

      return ERROR;      
   }

   if (sockfd > 0)
   {
      (void)close(sockfd);
      sockfd = 0;
   }

   sockfd = socket(AF_INET, SOCK_STREAM, 0);
   if ((sockfd == ERROR) && (errno > 0))
   {
      if (silent == 1)
         error("(in spool parent) error with socket(): %s\n\n", 
               strerror(errno));

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

      quit(ERROR);  
   }  
      
   setsockopts(sockfd, 1);

   if (locPort <= 0) locPort = LOCPORT;

   laddr.sin_family = AF_INET;
   laddr.sin_port = htons(locPort);
   laddr.sin_addr.s_addr = INADDR_ANY;

   if (hent == NULL) 
   {
      /* we're fucked.. so just spool locally */
      error("unable to resolve server's hostname.. now spooling\n\n");
      doSpooling();
   }

   daddr.sin_family = AF_INET;
   daddr.sin_port = htons(servList[curServ].port);
   daddr.sin_addr = *((struct in_addr *)hent->h_addr);

   memset(&(laddr.sin_zero), 0, 8), memset(&(daddr.sin_zero), 0, 8);

# if !defined(SUN) && !defined(BSD)
#  ifdef _POSIX_SAVED_IDS
   res = setuid(0);
#  else
   res = seteuid(0);
#  endif

   if (res == ERROR)
   {
      error("error setting [e]uid: %s\n\n", strerror(errno));
      quit(ERROR);
   }
# endif

   rbindport();

# if !defined(SUN) && !defined(BSD)
   if (pwd != NULL)
   {
#     ifdef _POSIX_SAVED_IDS
      res = setuid(pwd->pw_uid);
#     else
      res = seteuid(pwd->pw_uid);
#     endif

      if (res == ERROR)
      {
         error("error setting [e]uid: %s\n\n", strerror(errno));
         quit(ERROR);
      }
   }
# endif

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

      if (strstr(host, (char *)inet_ntoa(daddr.sin_addr)) != NULL)
         (void)printf("%connecting to %s...\n", 
                      (debugging == 1 ? 'c' : 'C'), host);      

      else
         (void)printf("%connecting to %s (%s)...\n", 
                      (debugging == 1 ? 'c' : 'C'),
                      host, (char *)inet_ntoa(daddr.sin_addr));
   }

   while(1)
   {
      if (connect(sockfd, (struct sockaddr *)&daddr,
                  sizeof(struct sockaddr)) == ERROR)
      {
         if (errno == EINTR) continue;

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

            error("error connecting: %s\n\n", strerror(errno));
         }

         return ERROR;   
      }

      else
      {
         debug("connect() succeeded...\n\n");
         break;
      }
   }

#ifndef NOSSL
# if !defined(SUN) && !defined(BSD)
#  ifdef _POSIX_SAVED_IDS
   res = setuid(0);
#  else
   res = seteuid(0);
#  endif

   if (res == ERROR)
   {
      error("error setting [e]uid: %s\n\n", strerror(errno));
      quit(ERROR);
   }
# endif

   makeSSLconn();

# if !defined(SUN) && !defined(BSD)
   if (pwd != NULL)
   {
#     ifdef _POSIX_SAVED_IDS
      res = setuid(pwd->pw_uid);
#     else
      res = seteuid(pwd->pw_uid);
#     endif

      if (res == ERROR)
      {
         error("error setting [e]uid: %s\n\n", strerror(errno)); 
         quit(ERROR);
      }
   }
# endif
#endif

   recv_data(readbuf, sizeof(readbuf));
   if (silent != 1) debug("checking for successful connect\n");
   
   if (strncmp(readbuf, "SUCCESSFUL connect", 18) == 0)
   {
      connected = 1;

      if ((silent == 1) && (spooling == 1))
      {
         silent = 0;
         (void)printf("(in spool parent) now connected to %s\n\n", host);
      }

      else (void)printf("%cow connected\n\n", (debugging == 1 ? 'n' : 'N'));

      return 0;
   }

   else
   {
      connected = 0;   

      if (silent == 1)
         error("(in spool parent) ERROR received from server\n"
               "error message: %s\n%c", readbuf,
               (strchr(readbuf, '\n') == NULL ? '\n' : '\0'));
     
      else
         error("ERROR received from server\nerror message: %s\n%c",
               readbuf, (strchr(readbuf, '\n') == NULL ? '\n' : '\0'));

      return ERROR;
   }
}


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


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

   if (all == 1)
   {
#     ifdef SO_KEEPALIVE
      /* val = 1; */
      res = setsockopt(fd, 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;

      res = setsockopt(fd, 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

      return;
   }

   else
   {
      val = 1;
      res = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&val, 
                       sizeof(val));

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

         quit(ERROR);
      }
   }

   return;
}
