// $Id: pdinrs_socket.c,v 1.1.1.1 2005/01/10 18:47:02 pdi Exp $
/******************************************************************************/
// Copyright 1998-2022 Stonie R. Cooper, Licensed under the auspices of GPLv3,
// See top-level copyright disclaimer (COPYRIGHT) and license (LICENSE)
// File:   open_socket.c
// Author: Stonie R. Cooper
// Documentation:
// Revision Record:
//         Rewritten: 20050114 by Stonie R. Cooper
//                  - created file and added includes and function declara-
//                    tion.
/******************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/select.h>

#include <pdinrs.h>
#include <pdinrs_distribution.h>

int open_socket(char *hostname, unsigned short int port, time_t seconds)
{
  int sockfd = 0;  
  int flags = 0;
  int error_length = 0;
  int error_code = 0;
  fd_set readfds;
  fd_set writefds;
  struct hostent *heptr = NULL;
  struct hostent hent;
  int herrno = 0;
  char hbuf[HE_SIZE];
  struct sockaddr_in their_addr; // connector's address information 
  struct timeval tm;

  tm.tv_sec = seconds / 15;
	if (tm.tv_sec < 1)
		tm.tv_sec = 1;
  tm.tv_usec = 0;

  memset((void *)&hent, 0, sizeof(hent));
  memset((void *)hbuf, 0, sizeof(hbuf));
// Create the socket

  if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) 
    return -1;

// Set the socket to non-blocking

  if ((flags = fcntl(sockfd, F_GETFL, 0)) < 0)
    return -1;

  if ((fcntl(sockfd, F_SETFL, flags | O_NONBLOCK)) < 0)
    return -1;

  FD_ZERO(&readfds);
  FD_SET(sockfd, &readfds);

  writefds = readfds;

// Get the host info - in this case, using the reentrant form for thread safety.

  if ((gethostbyname_r(hostname, &hent, hbuf, sizeof(hbuf), &heptr, &herrno)) != 0 || heptr == NULL)
    return -1;

  their_addr.sin_addr = *((struct in_addr *)heptr->h_addr);

  their_addr.sin_family = AF_INET;         // host byte order
  their_addr.sin_port = htons(port);     // short, network byte order
  bzero(&(their_addr.sin_zero), 8);        // zero the rest of the struct

// Connect to the server

  if ((error_code = connect(sockfd, (struct sockaddr *)&their_addr, sizeof(struct sockaddr))) < 0)
  {
    if (errno != EINPROGRESS)
    {
      close(sockfd);
      return -1;
    }
  }
  else
    return sockfd;
  
  if ((error_code = select(sockfd+1, &readfds, &writefds, NULL, &tm)) < 1)
  {
    if (error_code == 0)
      errno = ETIMEDOUT;
    close(sockfd);
    return -1;
  }

  if (FD_ISSET(sockfd, &readfds) || FD_ISSET(sockfd, &writefds))
  {
    error_length = sizeof(error_code);
    if ((getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error_code, (unsigned int *)&error_length)) < 0)
    {
      close(sockfd);
      return -1;
    }
  }
  else
  {
    close(sockfd);
    return -1;
  }

  if (error_code)
  {
    errno = error_code;
    close(sockfd);
    return -1;
  }

  return sockfd;
}

int Send(int fd, const char *vptr, size_t n, time_t seconds, unsigned int retries)
{
  int retry;
  int nleft;
  int nwritten;
  struct timeval tm;
  fd_set writefds;
  const char *ptr;
  time_t old_time = 0;
  time_t new_time = 0;
  
  retry = 0;
  ptr = vptr;
  nleft = n;
  time(&old_time);
  tm.tv_sec = (seconds / (3 * (1 + retries)));
	if (tm.tv_sec < 1) tm.tv_sec = 1;
  tm.tv_usec = 0;

  FD_ZERO(&writefds);
  FD_SET(fd, &writefds);


  select(fd+1, NULL, &writefds, NULL, &tm);


  if (FD_ISSET(fd, &writefds))
  {
    while (nleft > 0)
    {


      if ((nwritten = send(fd, ptr, nleft, 0)) <= 0)
      {


        if (errno == EINTR)
          nwritten = 0;
        else
        {
          time(&new_time);
          if (new_time < (old_time + seconds))
          {
            nwritten = 0;
          }
          else
          {
            return(-1);
          }
        }
      }
      nleft -= nwritten;
      ptr += nwritten;
    }
    return(n);
  }
  else
  {
    return(-1);
  }
}
