/*
  tcppingdaemon, replies to pings via tcp.
  after receiving one byte, replies by giving a byte back.
  15 Mar 2001 Junichi Uekawa
  22 Mar 2001 Junichi Uekawa, changed so that host generation is done by 
  one function only.

  nantarad-protocol. when requested, echoes back the value of 
  contents of the file (in ASCII format).
  25feb2001 Junichi Uekawa
  2 mar2001 Junichi Uekawa reused for nantarad.

  copyright 2001 Junichi Uekawa
  see GPL version 2 or later for license

  aiming to use it as a ping-echoback server to say if the machine has
  an open ppp connection.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/poll.h>
#include <netinet/in.h>
#include <netdb.h>
#include <pthread.h>
#include <time.h>

#define BACKLOG 5		/* max backlog to listen to */
#define WAIT_SECONDS 10.0       /* how many seconds to wait rereading */

struct handle_incoming_params
{
  int t ;			/* socket */
  struct sockaddr_in *tmpisap;	/* address of caller */
} handle_incoming_params ;

static void handle_incoming(struct handle_incoming_params * parameter)
{
  char buf [2] ;
  printf("Incoming [%i]\n", parameter->t);
  while ( read (parameter->t, buf, 1) == 1) 
    write (parameter->t, buf, 1);
  printf("Closed [%i]\n", parameter->t);  
  /* cleanup routine */
  close(parameter->t);  
  free (parameter->tmpisap);  
  free (parameter);
}

static char * gethostname_versatile(void)
{
  const int unithostnamelen = 256;
  int unit = 1;
  char * localhostname = malloc (unithostnamelen + 1) ;
  if (!localhostname) return NULL;
  
  while (gethostname (localhostname, unithostnamelen * unit) == -1)
    {
      char * newlocalhostname = realloc(localhostname, unithostnamelen * (++ unit) + 1);
      if (newlocalhostname == NULL)
	{
	  free(localhostname);
	  return NULL;
	}
      else 
	localhostname = newlocalhostname ;
    }
  return localhostname;
}

#define malloc_with_error(A,BYTES) if (NULL==((A)=malloc(BYTES)))\
{fprintf(stderr, "out of memory in allocating %i bytes\n", BYTES);return (1);}

				/* return 1 on error */
int 
tcp_host_setup( const char * programname, const char * port_name, void * handle_incoming_function)
{
  char *localhostname ;
  struct sockaddr_in sa;
  struct handle_incoming_params * params;
    
  int incoming_socket;		/* the main waiting socket */
  struct hostent *hp;
  int i;
  pthread_t newthread_id ;
  pthread_attr_t pthread_attrib ;
  
				/* creating a pthread attribute */
  pthread_attr_init(&pthread_attrib);
  pthread_attr_setdetachstate(&pthread_attrib,1);
  
  if (!(localhostname = gethostname_versatile ()))
    {
      fprintf(stderr, "%s: Cannot allocate enough memory to gethostname\n", programname);
      return 1;      
    }
  if ((hp = gethostbyname(localhostname)) == NULL)
    {
      fprintf(stderr, "%s: cannot get localhost info\n", programname);
      return 1;      
    }
  if ((incoming_socket = socket (hp->h_addrtype, SOCK_STREAM, 0 )) == -1 )
    {
      perror  ("socket");
      return 1;
    }

  sa.sin_port = htons(atoi(port_name));	/* this should hopefully be portable */
  sa.sin_addr.s_addr= htonl(INADDR_ANY); /* should it be like this? it's 0 in linux */
  sa.sin_family = AF_INET;
  
  if (bind(incoming_socket, (struct sockaddr*)&sa, sizeof (sa) ) < 0  ) 
    {
      perror ("bind");
      return 1;      
    }

  listen (incoming_socket, BACKLOG);
  /* the main routine getting incoming connections */
  while (1)
    {
      i = sizeof (struct sockaddr_in);
      malloc_with_error(params, sizeof(struct handle_incoming_params));
      malloc_with_error(params->tmpisap, i);
      if ((params->t = accept (incoming_socket, (struct sockaddr*)params->tmpisap, &i )) == -1)
	{
	  perror ("accept");
	  return 1;	  
	}      
      pthread_create (&newthread_id, &pthread_attrib, (void*)handle_incoming_function, params);
    }
}

int main (int ac, char** av)
{  
  puts ("tcpping daemon version 0.0.1\n"
	"copyright 2001 Junichi Uekawa\n");
  if (ac != 2)
    {
      fprintf(stderr, "Please give me [port number]\n");
      exit(EXIT_FAILURE);
    }
  exit (tcp_host_setup(av[0],av[1],handle_incoming));
}
