/* Copyright (c) 1991-2002 Doshita Lab. Speech Group, Kyoto University */
/* Copyright (c) 2000-2002 Speech and Acoustics Processing Lab., NAIST */
/*   All rights reserved   */

/* server-client.c --- functions for TCP/IP server & client */

/* $Id: server-client.c,v 1.4 2002/11/06 08:37:06 ri Exp $ */

#include <sent/stddefs.h>
#include <sent/tcpip.h>



/* Ready for receiving request from client. */
/* Return value: -1 ... fail to create socket descriptor
                 -2 ... fail to assign name to the socket
		 -3 ... fail to start listening
		 other ... socket descriptor ready for accept */
int
ready_as_server(int port_num)
{
  struct sockaddr_in sin;
  int sd;
  int optval;
  int optlen;

  /* create socket */
  if((sd = socket(PF_INET, SOCK_STREAM, 0)) < 0){
    perror("ready_as_server: socket() error") ;
    return -1;
  }

  /* set socket to allow reuse of local address at bind() */
  /* this option prevent from "error: Address already in use" */
  optval = 1;
  optlen = sizeof(int);
  if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (char *)&optval, optlen) != 0) {
    perror("ready_as_server: setsockopt() error");
    return -2;
  }

  /* assign name(address) to socket */
  memset((char *)&sin, 0, sizeof(sin));
  sin.sin_family = AF_INET;
  sin.sin_port = htons(port_num);
  if(bind(sd, (struct sockaddr *)&sin, sizeof(sin)) < 0){
    if (errno == EADDRINUSE) {	/* address already in use */ }
    perror("ready_as_server: bind() error") ;
    return -2;
  }
  /* begin to listen */
  if (listen(sd, 5) < 0) {
    perror("ready_as_server: listen() error");
    return -3;
  }
  return(sd);
}

/* Ready for receiving request from client. */
/* Created socket will be non-blocking. */
/* Return value: -1 ... fail to create socket descriptor
                 -2 ... fail to assign name to the socket
		 -3 ... fail to start listening
		 -4 ... fail to set socket non-blocking
		 other ... socket descriptor ready for accept */
int
ready_as_server_nonblock(int port_num)
{
  struct sockaddr_in sin;
  int sd;

  memset((char *)&sin, 0, sizeof(sin));
  sin.sin_family = AF_INET;
  sin.sin_port = htons(port_num);
  if((sd = socket(PF_INET, SOCK_STREAM, 0)) < 0){
    perror("ready_as_server_nonblock: socket() error") ;
    return -1;
  }
  if(bind(sd, (struct sockaddr *)&sin, sizeof(sin)) < 0){
    perror("ready_as_server_nonblock: bind() error") ;
    return -2;
  }
  if((fcntl(sd, F_SETFL , fcntl(sd,F_GETFL,0)|O_NDELAY ))== -1){
    perror("ready_as_server_nonblock: fcntl() error");
    return -4;
  }
  if (listen(sd, 5) < 0) {
    perror("ready_as_server_nonblock: listen() error");
    return -3;
  }
  return(sd);
}

/* Wait for request from client. */
/* If blocking, wait until request comes. */
/* If non-blocking, no request in queue is an error (negative value returns) */
/* returns accepted socket descriptor */
static struct sockaddr_in from;
#ifdef HAVE_SOCKLEN_T
static socklen_t nbyte;
#else  
static int nbyte;
#endif /* HAVE_SOCKLEN_T */
int
accept_from(int sd)
{
  int asd;
  
  asd = accept(sd, (struct sockaddr *)&from, &nbyte);
  if (asd < 0) {
    perror("accept_from: accept() error");
  }
  j_printf("connect from %s\n", inet_ntoa(from.sin_addr));
  return asd;
}
  

/* Send connection request to a server. */
/* Return value:
   -1 ... fail to create socket descripter
   -2 ... fail to connect
   -3 ... no such host */
int
make_connection(char *hostname, int port_num)
{
  static struct hostent *hp;
  static struct sockaddr_in	sin;
  int sd;
  int trynum;

  /* host existence check */
  if ((hp  = gethostbyname(hostname)) == NULL) {
    j_printerr("make_connection: host not found: %s\n",hostname);
    return -3;
  }

  /* try to connect */

  for (trynum = 0; trynum < CONNECTION_RETRY_TIMES; trynum++) {
    memset((char *)&sin, 0, sizeof(sin));
    memcpy(&sin.sin_addr, hp->h_addr, hp->h_length);
    sin.sin_family = hp->h_addrtype;
    sin.sin_port = htons(port_num);
    if((sd = socket(PF_INET, SOCK_STREAM, 0)) < 0){
      perror("make_connection: socket() error");
      return -1;
    }
    if (connect(sd, (struct sockaddr *)&sin, sizeof(sin)) >= 0) {
      /* success */
      break;
    } else {
      /* failure */
      perror("make_connection");
      /* retry */
      j_printerr("retry after %d second...\n", CONNECTION_RETRY_INTERVAL);
      sleep(CONNECTION_RETRY_INTERVAL);
    }
  }
  if (trynum == CONNECTION_RETRY_TIMES) {
    /* finally failed */
    j_printerr("make_connection: failed to connect to $s:%d\n", hostname, port_num);
    return -2;
  }

  return sd;
}

/* Send connection request to a server (via UNIX DOMAIN SOCKET). */
/* Return value:
   -1 ... fail to create socket descripter
   -2 ... fail to connect */
int
make_connection_unix(
     char *address)		/* unix domain socket address (path) */
{
  struct sockaddr_un	ps;
  int len;
  int sd;

  ps.sun_family = PF_UNIX;
  strcpy(ps.sun_path, address);
  len = sizeof(ps.sun_family) + strlen(ps.sun_path);

  if((sd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0){
    perror("make_connection_unix: socket() error");
    return -1;
  }
  while(connect(sd, (struct sockaddr *)&ps, len) < 0){
    perror("make_conenction_unix: connect() error");
    /* retry */
    j_printerr("retry after %d sec...\n",CONNECTION_RETRY_INTERVAL);
    sleep(CONNECTION_RETRY_INTERVAL);
  }
  
  return sd;
}
