/*
  File:  accept_client.c 

  Description:  function to accept a pending connection from a client.
*/

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include "proxy_protos.h"

extern int errno;


void
accept_client ( struct kevent *ke, struct mdata *md )
{
  int                  client;
  int                  server;
  struct kevent        sk[2];
  socklen_t            sl;
  struct sockaddr_in   sa;
  int                  cn;

  /* first test and see if we're out of memory.  if we are, then don't
     accept any more clients until we get some memory. */

  if ( md->out_of_memory ) {
    log_msg(md,"pending connection ignored (insufficient memory)");
    return;
  }

  /* accept a pending connection */

  sl = sizeof(sa);

  if ( (client = accept(ke->ident,(struct sockaddr *)&sa,&sl)) != -1 ) {

    inet_ntop(AF_INET,&sa.sin_addr,md->r[client].ip,INET_ADDRSTRLEN);

    /* make a socket for connecting to the server */

    if ( (server = socket(PF_INET,SOCK_STREAM,0)) != -1 ) {

      /* desire nonblocking I/O both ways including nonblocking connect */
      
      if ( fcntl(client,F_SETFL,O_NONBLOCK) == -1 ) perror("fcntl");
      if ( fcntl(server,F_SETFL,O_NONBLOCK) == -1 ) perror("fcntl");
      
      /* attempt to open the connection to the server (nonblocking) */

      cn = connect(server,(struct sockaddr *)&md->sa,sizeof(md->sa));

      /* record the time at which the connection was started */

      gettimeofday(&md->r[server].ti_connect,NULL);      

      /* 
	 either the connection is in progress, or it succeeded 
	 immediately, or it failed immediately.  
      */
	 
      if ( cn == -1 && errno == EINPROGRESS ) {
	md->r[server].status = FD_CLOSE;
      } else if ( cn == 0 ) {
	md->r[server].status = FD_OPEN;
	gettimeofday(&md->r[server].tf_connect,NULL);      
	log_msg(md,"Connect succeeded immediately on [fd %d]: %.6f seconds",
		server,diff_timeval(&md->r[server].ti_connect,
				    &md->r[server].tf_connect));
      } else {
	server = -1;
      }

      /* 
	 if we're either on the way to making a connection or we've
	 succeeded in making one, set everything up. 
      */

      if ( server != -1 ) {

	log_msg(md,"[%s] connecting to [%s]",
		md->r[client].ip,md->server_name);

	/* connections are both open */

	md->r[client].status   = FD_OPEN;

	/* set the relay file descriptors */
	
	md->r[client].relay    = server;
	md->r[server].relay    = client;

	/* set the connection types */

	md->r[client].type     = CLIENT;
	md->r[server].type     = SERVER;

	/* increment the number of concurrent clients */
	
	md->num_clients++;

	/* possibly a new maximum number of concurrent clients */

	if ( md->num_clients > md->max_clients ) 
	  md->max_clients = md->num_clients;

	/* a kevent for reading data from the client */
	
	sk[0].ident   = client;
	sk[0].filter  = EVFILT_READ;
	sk[0].flags   = EV_ADD;
	sk[0].udata   = read_data;
	
	/* a kevent for reading data from the server */
	
	sk[1].ident   = server;
	sk[1].filter  = EVFILT_READ;
	sk[1].flags   = EV_ADD;
	sk[1].udata   = read_data;
	
	/* register both events, record an error if failure */

	if ( kevent(md->kq,sk,2,NULL,0,NULL) != -1 ) {
	  md->accepts++;
	} else {
	  perror("kevent");
	  md->kevent_errors++;
	}

      } else {

	/* connect failed */

	perror("connect");

      }

    } else {

      /* unable to create a socket for server connection */

      perror("socket");

    }

    /* if connection to server failed, close connection to client */

    if ( server == -1 ) close ( client );

  } else {    

    /* could not accept connection from client */

    perror("accept");

  }

}



syntax highlighted by Code2HTML, v. 0.9.1