/*
 * Copyright 1994 David A. Barrett.
 *
 * Connect to a remote login program which requires response to a crypto
 * challenge.  Communicate subsequently via a DES encrypted channel.
 */
#define _POSIX_SOURCE
#include <unistd.h>	/* close getlogin */
#include <stdlib.h>	/* atexit exit */
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>	/* for fcntl.h */
#include <fcntl.h>
#include <termios.h>
#include "posignal.h"
#include "tty.h"
#include "auth.h"
#include "txfr.h"
#include "socket.h"
#include "deslogin.h"
#include "utmp.h"

extern int  optind;
extern char *optarg;

#if defined(__STDC__) || defined(__cplusplus)
extern int askTty(char *, char *, unsigned, int);
#else
extern int askTty();
#endif

static char ident[] = 
" @(#) deslogin.c   version 1.32 03-Mar-95 Copyright 1995 by Dave Barrett(barrett@asgard.cs.Colorado.EDU)\n";
static char RCSid[] = 
   " @(#) deslogin.c  RCS: $Revision: 1.8 $ $Date: 95/03/08 01:13:04 $\n";

char *progName;
int  debug = 0;
int  verbose = 0;

char unknownLoginName[] = "unknown";

unsigned long inBytes = 0;		/* for txfr */
unsigned long outBytes = 0;

struct termios oldmodes;
int fixfd = -1;

void fixtty()
{
   register int res;

   if (fixfd >= 0) {
      res = restoreTty(fixfd, &oldmodes);
   }
}

void handler(sig)
   int sig;
{
   exit(0);		/* calls fixtty via atexit */
}


/*
 * Perform a client handshake with the server
 */
char *clientHandshake(nfd, string1, string2)
   int nfd;
   char *string1, *string2;
{
   int count, res;
   static char protover[VERS_SIZE];

   count = getString(nfd, protover, VERS_SIZE-1, SETUP_TIMEOUT);
   if (count == 0) {
      fprintf(stderr, "%s: network read of (protover) failed--%s\n", 
	  progName, ERRMSG);
      return 0;
   }
   res = write(nfd, PROTOCOL_VERS, strlen(PROTOCOL_VERS)+1);
   if (res < 0) {
      fprintf(stderr, "%s: gateway network write of \"%s\" failed--%s\n", 
	 progName, PROTOCOL_VERS, ERRMSG);
      return 0;
   }
   if (protover[0] != PROTOCOL_VERS[0]) {
      fprintf(stderr, 
	 "%s: remote protocol version (%s) incompatable with local (%s)\n",
	  progName, protover, PROTOCOL_VERS);
      return 0;
   }
   res = write(nfd, string1, strlen(string1)+1);
   if (res < 0) {
      fprintf(stderr, "%s: gateway network write of \"%s\" failed--%s\n", 
	 progName, string1, ERRMSG);
      return 0;
   }
   res = write(nfd, string2, strlen(string2)+1);
   if (res < 0) {
      fprintf(stderr, "%s: gateway network write of \"%s\" failed--%s\n", 
	  progName, string2, ERRMSG);
      return 0;
   }
   return protover;
}

int main(argc, argv)
   int argc;
   char *argv[];
{
   int ch, count, res;
   unsigned bufSize = 127;
   char aport[16];
   char *chp, *hostName = (char *) 0, *gateway = (char *) 0;
   char *loginName, *ruser = (char *) 0;
   int nfd, port = DESLOGIN_PORT, gport = 0, loginLen, userLen;
   char passPhrase[PHRASE_SIZE], target[128], *tp = (char *) 0;
   keyType key;

   progName = *argv;
   if ((chp = strrchr(progName, '/')) != (char *) 0) progName = chp + 1;

   while ((ch = getopt(argc, argv, "dvh:g:")) != EOF) switch (ch) {
   case 'h':
      tp = optarg;
      break;
   case 'g':
      gateway = optarg;
      break;
   case 'v':
      verbose++;
      break;
   case 'd':
      debug++;
      break;
   default:
usage:
      fprintf(stderr, 
	 "usage: %s [-dvt] [-g gateway] [ [user@]domain[:port] ]\n", 
	 progName);
      exit(1);
   }
   argc -= optind;
   argv += optind;

   if (argc != 0) {
      tp = *argv++; --argc;
   }

   if (tp == (char *) 0) {
      res = askTty("Input destination <[user@]domain[:port]>:", 
	 target, 128-1, 1);
      if (res < 0) {
	 fprintf(stderr, "%s: couldn't get destination\n", progName);
	 exit(1);
      }
      tp = target;
   }

   chp = strchr(tp, '@');
   if (chp != (char *) 0) {
      *chp = '\0';
      ruser     = tp;
      hostName  = chp+1;
   } else {
      hostName  = tp;
   }

   chp = strchr(hostName, ':');
   if (chp != (char *) 0) {
      *chp = '\0';
      count = sscanf(chp+1, "%u", &port);
      if (count != 1) goto usage;
   }
   sprintf(aport, "%d", port);

   loginName = (char *) getLoginName();		/* NOT getlogin!; see utmp.c */
   if (loginName == (char *) 0) {
      fprintf(stderr, "%s: Warning--getLoginName failed\n", progName);
      loginName = unknownLoginName;
   }
   loginLen = strlen(loginName);

   if (ruser == (char *) 0) {
      ruser = loginName;
      if (loginName == unknownLoginName) {
	 fprintf(stderr, 
	 "%s: unknown local login name; must specify remote user\n", progName);
	 exit(1);
      }
   }
   userLen = strlen(ruser);

   if (gateway != (char *) 0) {
      chp = strchr(gateway, ':');
      if (chp != (char *) 0) {
	 *chp = '\0';
	 count = sscanf(chp+1, "%u", &gport);
	 if (count != 1) goto usage;
      }
      if (gport == 0) {
	 gport = getServicePort(GW_SERVICE_NAME);
	 if ((int) gport < 0) {
	    gport = DESLOGIN_GW_PORT;
	 }
      }

      nfd = openClient(gateway, gport);
      if (nfd < 0) {
	 fprintf(stderr, "%s: open gateway %s:%d failed --%s\n", 
	    progName, gateway, gport, ERRMSG);
	 exit(1);
      }
      res = askTty("Gateway Pass phrase: ", passPhrase, PHRASE_SIZE-1, 0);
      if (res < 0) {
	 fprintf(stderr, "%s: couldn't get pass phrase\n", progName);
	 exit(1);
      }
      key = signature(nfd, passPhrase, SETUP_TIMEOUT);
      memset(passPhrase, '\0', PHRASE_SIZE);
      destroyKey(&key);

      chp = clientHandshake(nfd, hostName, aport);
      if (!chp) {
	 exit(1);
      }
   } else {
      nfd = openClient(hostName, port);
      if (nfd < 0) {
	 exit(1);
      }
   }

   chp = clientHandshake(nfd, loginName, ruser);
   if (!chp) {
      exit(1);
   }
   res = askTty("Pass phrase: ", passPhrase, PHRASE_SIZE-1, 0);
   if (res < 0) {
      fprintf(stderr, "%s: couldn't get pass phrase\n", progName);
      exit(1);
   }
   key = signature(nfd, passPhrase, SETUP_TIMEOUT);
   memset(passPhrase, '\0', PHRASE_SIZE);

   /*
    * If unsuccessful, the remote side will shut down the connection which
    * will cause the rest of the program to exit
    *
    * There is a strange interaction between two fd's associated with the
    * same device.  They will share the same modes.  Thus, only one of
    * these modes must be saved or restored.
    */
   if (isTty(0)) {
      fixfd = 0;
   } else if (isTty(1)) {
      fixfd = 1;
   }
   if (fixfd >= 0) {
      res = setTtyBin(fixfd, &oldmodes);
      if (res < 0) {
	 fprintf(stderr, "%s: setTtyBin failed to fd %d--%s\n", 
	    progName, fixfd, ERRMSG);
	 exit(1);
      }
      res = atexit(fixtty);
      if (res == -1) {
	 fprintf(stderr, "%s: atexit failed--%s\n", progName, ERRMSG);
	 exit(1);
      }
   }

   chp = (char *) posignal(SIGINT, handler);
   if (chp == (char *) -1L) {
      fprintf(stderr, "%s: sigaction failed--%s\n", progName, ERRMSG);
      exit(1);
   }

   res = txfr(nfd, 0, 1, bufSize, 0, key);
   destroyKey(&key);

   if (isTty(1)) {
      write(1, "\r\nConnection closed.\r\n", 22);
   }
   exit(0);			/* to ensure that atexit is called */
   return 0;
}
