/* inquir:/u/sra/INQUIR/emacs-ui/client.c, 5-Sep-1990 01:05:24, sra 
 * krb error fixed, client renamed to /usr/site/watson and 
 * source moved to /usr/src/site/inquir/inquir.c 3-9-92 jhbrown
 *
 * lots and lots of piddly changes, improved error-handling, 
 * name changed to inquir-backend.c and now lives in /common/inquir/src/
 * A million thanks to metcalf for all his debugging help.
 * - jhbrown 7-31-92
 */


#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <errno.h>

#ifdef INQUIR_KRB
#include <krb.h>
extern char *getenv(const char *);

#define INQUIR_PRINCIPAL "inquir"
#define INQUIR_INSTANCE "inquir"
#endif INQUIR_KRB

#ifndef	HOLMES_HOST
#define	HOLMES_HOST	"python"
#endif

static FILE *neti = NULL, *neto = NULL;
static char *jname = NULL;

static void update();
static void fatal(char *msg);
static void usage(void);
static void tcpmux(char *contact);
static void tcpmuxclose();

#ifdef INQUIR_KRB
static char krberr[256] = "";
#endif



int main(int argc, char *argv[])
{
    int c;
    
    jname = argv[0];
    if (argc < 2 || argv[1][0] != '-')
	usage();
    switch (argv[1][1]) {
      case 'c':
	if (argc != 2)
	    usage();
	tcpmux("inquir-config");
	break;
      case 'l':
	if (argc != 3)
	    usage();
	tcpmux("inquir-lookup");
	if (fprintf(neto, "%s\r\n", argv[2]) == EOF)
	    fatal("fprintf");
	break;
      case 'u':
	if (argc != 2)
	    usage();
	update();
	break;
      case 'n':
	if (argc != 2)
	  usage();
	tcpmux("inquir-new-uid");
	break;
      default:
	usage();
    }
    /*
     * Just closing neto doesn't send a FIN packet, sigh.  Hit outbound
     * connection with large blunt object, sparing inbound connection.
     */
    if (fflush(neto) == EOF)
	fatal("fflush");
    if (shutdown(fileno(neto), 1))
	fatal("shutdown");
    if (fclose(neto) == EOF)
	fatal("fclose");
    else
      neto = NULL;

#ifdef INQUIR_KRB
    if (fputc((c=getc(neti)), stdout) != '+')
      if (krberr[0]) {
	puts(krberr);
	puts("Tried non-KERBEROS update, failed with:\r\n");
      }
#endif

    while((c = getc(neti)) != EOF)
	putchar(c);
    if (fclose(neti) == EOF)
	fatal("fclose");
    else
      neti = NULL;
    return 0;
}

/* rob: I know this is gross, but I'm not sure what to do and how much
   cleanliness is valued in this perl-db hack to start with  -gdt */
#define BUFSIZE	32768

#ifdef INQUIR_KRB
static struct sockaddr_in serveraddr;
static struct sockaddr_in myaddr;
#endif


/* 
 * Try to do update using kerberos, and fall back to regular way
 * if this fails.
 */
static void update()
{
  int i;
  int len, netlen, privlen;
  char c;
  char inbuf[BUFSIZE];
  char netbuf[BUFSIZE];

#ifdef INQUIR_KRB
  char princ[ANAME_SZ+1];
  KTEXT_ST auth_msg;
  CREDENTIALS cred;
  des_key_schedule sched;
  char *tfname = getenv("KRBTKFILE");
#endif

  len = fread(inbuf, sizeof(char), BUFSIZE, stdin);

  if ( len < 0 )
    {
      fatal("fread in update");
    }

#ifdef INQUIR_KRB

  printf("CLB: checking kerberos tickets\n" );

  /* check for tickets */
  if (tfname == NULL ||
      krb_get_tf_fullname(tfname, princ, 0, 0) != KSUCCESS)
    {
      printf("failure 1\n");
      goto non_krb_update;
    }

  tcpmux("inquir-update-krb");

  fflush(neto);			/* we will not use stdio */

  /* build authenticator */
  i = krb_mk_req(&auth_msg,
		 INQUIR_PRINCIPAL, INQUIR_INSTANCE,
		 krb_realmofhost(HOLMES_HOST), 0);
  if ( i != KSUCCESS )
    {
      goto non_krb_update;
    }

  /* send length, and then auth data */
  netlen = htonl(auth_msg.length);
  i = write(fileno(neto), &netlen, sizeof(int));
  if ( i != sizeof(int) )
    {
      goto non_krb_update;
    }
  i = write(fileno(neto), auth_msg.dat, auth_msg.length);
  if ( i != auth_msg.length )
    {
      goto non_krb_update;
    }

  /* fetch session key */
  i = krb_get_cred(INQUIR_PRINCIPAL, INQUIR_INSTANCE,
		 krb_realmofhost(HOLMES_HOST), &cred);
  if ( i != KSUCCESS )
    {
      goto non_krb_update;
    }
  des_key_sched(cred.session, sched);

  /* obtain my internet address */
  {
    int mylen;

    mylen = sizeof (myaddr);
    if (getsockname (fileno(neto), (struct sockaddr *) &myaddr, &mylen) < 0)
      {
	goto non_krb_update;
      }
   }


  /* make private message */
  privlen = krb_mk_priv(inbuf, netbuf, len,
		  sched, cred.session,
		  &myaddr, &serveraddr);
  if ( privlen < 0 )
    {
      goto non_krb_update;
    }

  netlen = htonl(privlen);
  i = write(fileno(neto), &netlen, sizeof(int));
  if ( i != sizeof(int) )
    {
      goto non_krb_update;
    }
  i = write(fileno(neto), netbuf, privlen);
  if ( i != privlen )
    {
      goto non_krb_update;
    }

  /* check return code */
  if (fflush(neto) == EOF)
    {
      fatal("fflush");
    }
  if (shutdown(fileno(neto), 1))
    {
      fatal("shutdown");
    }
  if (fclose(neto) == EOF)
    {
      fatal("fclose");
    }
  else
    neto = NULL;

  c = getc(neti);
  if ( c == '+' )
    {
      putchar(c);
      while((c = getc(neti)) != EOF)
	putchar(c);
      tcpmuxclose();
      exit();
    }
  
  i = 0;
  while ((c=getc(neti)) != EOF)
    krberr[i++] = c;
  /* lost, fall through */
  krberr[i] = 0;
  printf( "fall through error\n" );

non_krb_update:
  if (!krberr[0]) 
    strcpy(krberr,"-KERBEROS error, unable to authenticate\r\n");
  tcpmuxclose();
#endif


  tcpmux("inquir-update");

  fwrite(inbuf, sizeof(char), len, neto);
  /* deal with return code */
}




static void tcpmux(char *contact)
{
    struct hostent *hp;
    struct servent *sp;
    struct sockaddr_in sin;
    int s, lost, c;

    if ((hp = gethostbyname(HOLMES_HOST)) == NULL)
	fatal("gethostbyname");
    bcopy(hp->h_addr, (char *) &sin.sin_addr, hp->h_length);
    if ((sp = getservbyname("tcpmux", "tcp")) == NULL)
	sin.sin_port = htons(1);
    else
	sin.sin_port = sp->s_port;

#ifdef INQUIR_KRB
    bcopy(&sin, &serveraddr, sizeof(struct sockaddr_in));
#endif

    if ((s = socket((sin.sin_family = hp->h_addrtype), SOCK_STREAM, 0)) < 0)
	fatal("socket");
    if (connect(s, (char *) &sin, sizeof(sin)) < 0)
	fatal("connect");
    if ((neti = fdopen(s,"r")) == NULL)
	fatal("fdopen");
    if ((s = dup(s)) < 0)
	fatal("dup");
    if ((neto = fdopen(dup(s),"w")) == NULL)
	fatal("fdopen");
    if (fprintf(neto, "%s\r\n", contact) == EOF)
	fatal("fprintf");
    if (fflush(neto) == EOF)
	fatal("fflush");
    if ((lost = (c = getc(neti)) != '+') && c != EOF)
	putchar(c);
    while ((c = getc(neti)) != EOF) {
	if (lost)
	    putchar(c);
	if (c == '\n')
	    break;
    }
    if (lost)
	fatal("lost");
}

static void tcpmuxclose()
{
  if ( neti != NULL )
    {
      fclose(neti);
      neti = NULL;
    }
  if ( neto != NULL )
    {
      fclose(neto);
      neto = NULL;
    }
}



static void fatal(char *msg)
{
  perror(msg);
    exit(1);
}



static void usage()
{
    printf("- Usage: %s [-c] [-l uname] [-u] [-n]\r\n", jname);
    exit(1);
}
