// mostly borrowed from freebie code

#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "emlen.h"
#ifdef WINDOWS

void start_auth (struct descriptor_data *d) {return;}
void read_auth (struct descriptor_data *d) {return;}
void send_auth (struct descriptor_data *d) {return;}

#else

#include "mush.h"
#include <sys/types.h>
#include <ctype.h>
#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <arpa/telnet.h>
#include <errno.h>
#include <netdb.h>
void nonblock (int s);
void start_auth (struct descriptor_data *d);
void read_auth (struct descriptor_data *d);
void send_auth (struct descriptor_data *d);


void 
start_auth (struct descriptor_data *d)
{
  struct sockaddr_in sock;
  int err;			/* error & result stuffs */
  int tlen;

  /*if ( !str_prefix( "130.63.236", d->host ) 
     || !str_prefix( "130.63", d->host ) )
     {
     free_string(d->user);
     d->user = str_dup( "(ncsa format)" );
     return;
     } */

  d->auth_fd = socket (AF_INET, SOCK_STREAM, 0);
  err = errno;

  if (d->auth_fd < 0 && err == EAGAIN)
    bug ("Can't allocate fd for authorization check", 0);
  nonblock (d->auth_fd);

  /* Clone incoming host address */
  tlen = sizeof (sock);
  getpeername (d->descriptor, (struct sockaddr *) &sock, &tlen);
  sock.sin_port = htons (113);
  sock.sin_family = AF_INET;

  if ((connect (d->auth_fd, (struct sockaddr *) &sock, sizeof (sock)) == -1)
      && (errno != EINPROGRESS))
    {
      /* Identd Denied */
      close (d->auth_fd);
      free_string (d->username);
      d->username = str_dup ("no_inetd");
      d->auth_fd = -1;
      d->auth_state = 0;
      d->atimes = 500;
      return;
    }

  if (errno == ECONNREFUSED)
    {
      close (d->auth_fd);
      d->auth_fd = -1;
      free_string (d->username);
      d->username = str_dup ("no_inetd");
      d->auth_state = 0;
      d->atimes = 500;
      return;
    }

  d->auth_state |= (WRITE_AUTH | AUTH);		/* Successful, but not sent */
  return;
}

/* send_auth */

void 
send_auth (struct descriptor_data *d)
{
  struct sockaddr_in us, them;
  char authbuf[32];
  int ulen, tlen, z;

  tlen = ulen = sizeof (us);

  if (getsockname (d->descriptor, (struct sockaddr *) &us, &ulen)
      || getpeername (d->descriptor, (struct sockaddr *) &them, &tlen))
    {
      bug ("auth getsockname error", 0);
      goto authsenderr;
    }

  /* compose request */
  sprintf (authbuf, "%u , %u\r\n",
	   (unsigned int) ntohs (them.sin_port),
	   (unsigned int) ntohs (us.sin_port));

/*  
   sprintf( log_buf, "sending [%u , %u] to auth port %d:113",
   (unsigned int)ntohs(them.sin_port),
   (unsigned int)ntohs(us.sin_port), d->auth_fd );
   log( log_buf, 0 );
 */

  nonblock (d->auth_fd);
  z = write (d->auth_fd, authbuf, strlen (authbuf));

  if (errno == ECONNREFUSED || (z == -1 && d->atimes > 10))
    {
      close (d->auth_fd);
      d->auth_fd = -1;
      free_string (d->username);
      d->username = str_dup ("no_identd");
      d->auth_state = 0;
      return;
    }

  if (z != strlen (authbuf))
    {
      if (d->atimes >= 15)
	{
	  sprintf (log_buf, "auth request, broken pipe [%d/%d]", z, errno);
	  bug (log_buf, 0);
	  close (d->auth_fd);
	  d->auth_fd = -1;
	  d->auth_state -= WRITE_AUTH;
	  d->auth_state &= ~AUTH;
	  d->auth_inc = 0;
	}
    authsenderr:
      d->atimes++;
      return;
    }
  d->auth_state -= WRITE_AUTH;	/* Successfully sent request */
  d->atimes = 0;
  return;
}

/* read_auth */
void 
read_auth (struct descriptor_data *d)
{
  char *s, *t;
  int len;			/* length read */
  char ruser[100], system[100];	/* remote userid */
  u_short remp = 0, locp = 0;	/* remote port, local port */
  *system = *ruser = '\0';

  /*
   * Can't allow any other reads from client fd while waiting on the
   * authfd to return a full valid string.  Use the client's input buffer
   * to buffer the authd reply.  May take more than one read.
   */

  nonblock (d->auth_fd);
  if ((len = read (d->auth_fd, d->abuf + d->auth_inc,
		   sizeof (d->abuf) - 1 - d->auth_inc)) >= 0)
    {
      d->auth_inc += len;
      if (d->auth_inc > 90)
	{
	  close (d->auth_fd);
	  d->auth_state = 0;
	  d->auth_fd = -1;
	  free_string (d->username);
	  d->username = str_dup ("no_identd");
	  d->atimes = 500;
	  return;
	}
      d->abuf[d->auth_inc] = '\0';
    }
  if (d->atimes < 20)
    {
      d->atimes++;
      return;
    }
  if (d->abuf[0] != '\0')

    if (
	 (sscanf (d->abuf, "%hd , %hd : USERID : %*[^:]: %10s",
		  &remp, &locp, ruser) == 3))
      {
	s = rindex (d->abuf, ':');
	*s++ = '\0';
	for (t = (rindex (d->abuf, ':') + 1); *t; t++)
	  if (!isspace (*t))
	    break;
	strncpy (system, t, sizeof (system));

	if (!str_prefix ("OTHER", system))
	  {
	    close (d->auth_fd);
	    d->auth_state = 0;
	    d->auth_fd = -1;
	    free_string (d->username);
	    d->username = str_dup ("no_identd");
	    d->atimes = 500;
	    return;
	  }

	for (t = ruser; *s && (t < ruser + sizeof (ruser)); s++)
	  if (!isspace (*s) && *s != ':')
	    *t++ = *s;
	*t = '\0';

	sprintf (log_buf, "auth reply ok, incoming user: [%s]", ruser);
	log_string (log_buf);
      }
    else if (len != 0)
      {
	if (!index (d->abuf, '\n') && !index (d->abuf, '\r'))
	  return;
	sprintf (log_buf, "bad auth reply: %s", d->abuf);
	*ruser = '\0';
      }
  close (d->auth_fd);
  d->auth_inc = 0;
  *d->abuf = '\0';
  d->auth_fd = -1;
  d->auth_state = 0;
  if (ruser[0] == '\0')
    strcpy (ruser, "no_inetd");
  free_string (d->username);
  d->username = str_dup (ruser);
  return;
}

#ifdef WINDOWS
void 
nonblock (int s)
{
  if (fcntl (s, F_SETFL, FNDELAY) == -1)
    {
      perror ("Noblock");
/*    exit(2); */
      bug ("Noblock", 0);
    }
}
#endif

#endif