/*
 * dip		A program for handling dialup IP connecions.
 *		Incoming connection handling module.
 *
 * Version:	@(#)login.c	3.3.6	07/15/94
 *
 * Author:      Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
 *		Copyright 1988-1993 MicroWalt Corporation
 *
 * Modified:    Uri Blumenthal, <uri@watson.ibm.com>
 *              (C) 1994
 *
 *		This program is free software; you can redistribute it
 *		and/or  modify it under  the terms of  the GNU General
 *		Public  License as  published  by  the  Free  Software
 *		Foundation;  either  version 2 of the License, or  (at
 *		your option) any later version.
 */
#include "dip.h"

static int verify_inet_addr(char *p);

static struct dip *
getdipnam(char *who)
{
  static struct dip dip;
  char buff[1024];
  register FILE *fp;
  register char *sp;
  char *bp;

  if ((fp = fopen(_PATH_ETC_DIPHOSTS, "r")) == (FILE *)NULL) {
	fprintf(stderr, "dip: cannot open %s\n", _PATH_ETC_DIPHOSTS);
	syslog(LOG_ERR, "cannot open %s", _PATH_ETC_DIPHOSTS);
	return((struct dip *)NULL);
  }

  while(fgets(buff, 1024, fp) != (char *)NULL) {
	if ((sp = strchr(buff, '\n')) != (char *)NULL) *sp = '\0';
	if (buff[0] == '#' || buff[0] == '\0') continue;

	sp = buff;
	memset((char *) &dip, 0, sizeof(struct dip));

	bp = sp;
	while (*sp && *sp != ':') sp++;
	*sp++ = '\0';
#if 1
	/* accept this diphosts record if either id or */
	/* ttyname is in first field                   */
	if (strcmp(bp, who) && strcmp(bp, ttyname(0)) &&
	    strcmp(bp, (rindex(ttyname(0), '/')+ 1))
           ) 
	  continue;
#else
	if (strcmp(bp, who)) continue;
#endif
	strncpy(dip.name, bp, 16);

	bp = sp;
	while (*sp && *sp != ':') sp++;
	*sp++ = '\0';
	strncpy(dip.passwd, bp, 8);

	bp = sp;
	while (*sp && *sp != ':') sp++;
	*sp++ = '\0';
	strncpy(dip.remote, bp, 128);

	bp = sp;
	while (*sp && *sp != ':') sp++;
	*sp++ = '\0';
	if (*bp)
	   strncpy(dip.local, bp, 128);

	bp = sp;
	while (*sp && *sp != ':') sp++;
	*sp++ = '\0';
	strncpy(dip.netmask, bp, 128);
	
	bp = sp;
	while (*sp && *sp != ':') sp++;
	*sp++ = '\0';
	strncpy(dip.comment, bp, 128);

	bp = sp;
	while (*sp && *sp != ':' && *sp != ',') sp++;
	*sp++ = '\0';
	strncpy(dip.protocol, bp, 16);

	bp = sp;
	while (*sp && *sp != ',') sp++;
	*sp++ = '\0';
	dip.mtu = atoi(bp);

	(void) fclose(fp);
	return(&dip);
  }
  (void) fclose(fp);
  return((struct dip *)NULL);
}

static char buff[256];

void
do_login(char *name, char **argp)
{
  struct dip *dip;
  struct hostent *hp;
  int i;
  extern char *h_errlist[];
  extern int h_errno;

  (void) openlog("dip", LOG_PID, LOG_DAEMON);
  bzero(buff, sizeof(buff));

  dip = getdipnam(name);
  if (dip == (struct dip *)NULL) {
	fprintf(stderr, "You do not have DIP access.  Go away.\n");
	syslog(LOG_WARNING, "%s tried to access DIP: no access!", name);
	exit(-1);
  }

#ifdef SKEY
  /* check if the user has S/Key authentication */
  if (strcmp(dip->passwd, SKEY_TOKEN) == 0) {
      int	i;

      /* Make sure that the user has an S/Key entry - if not
	 log a warning, and kick them off */
      if ((i = skey_haskey(name)) != SKEY_SUCCESS) {
	  fprintf(stderr, "Cannot find your S/Key entry.\n");
	  syslog(LOG_WARNING, "S/Key lookup failure for %s (%d)", 
		 name, i);
	  exit(1);
      }
	  
      if (skey_authenticate(name) != SKEY_SUCCESS) {
	  fprintf(stderr, "S/Key authentication failure.\n");
	  syslog(LOG_WARNING, "S/Key authentication failure for %s", name);
	  exit(-1);
      }

  }
#endif /* SKEY */

  /* Resolve this caller's host name to an IP address. */
  if ((hp = gethostbyname(dip->remote)) == (struct hostent *)NULL) {
	syslog(LOG_ERR, "unknown host %s: %s\n",
			dip->remote, h_errlist[h_errno]);
	exit(-1);
  }
  strncpy(dip->remote, hp->h_name, 128);
  memcpy((char *) &dip->rmt_ip, (char *) hp->h_addr_list[0], hp->h_length);

  /* Resolve the local name to what IP address server should have */
  if ((hp = gethostbyname(dip->local)) == (struct hostent *)NULL) {
	syslog(LOG_ERR, "unknown local host %s: %s\n",
			dip->local, h_errlist[h_errno]);
	exit(-1);
  }
  strncpy(dip->local, hp->h_name, 128);
  memcpy((char *) &dip->loc_ip, (char *) hp->h_addr_list[0], hp->h_length);

  /* Make sure the netmask is reasonably valid. And if not given, */
  /* make it 255.255.255.0, just like what I have in my subnet.   */
  if ((dip->netmask[0] == '\0') || (verify_inet_addr(dip->netmask) != 0))
    strcpy(dip->netmask, "255.255.255.0");
  
  /* Find out which protocol we have to use. */
  if ((i = get_prot(dip->protocol)) == 0) {
	fprintf(stderr, "dip: unknown protocol %s\n", dip->protocol);
	syslog(LOG_ERR, "%s wants unknown protocol %s",
					dip->remote, dip->protocol);
	exit(-1);
  }
  dip->protonr = i;

  /* Show some info. */
  if (opt_v == 1) {
	printf("Hostname: \"%s\" [%s]\n", dip->remote, inet_ntoa(dip->rmt_ip));
	printf("Local   : \"%s\" [%s]\n", dip->local,  inet_ntoa(dip->loc_ip));
	printf("Netmask : \"%s\"\n",      dip->netmask);
	printf("Comments: \"%s\"\n",      dip->comment);
	printf("Protocol: \"%s\" (%d)\n", dip->protocol, dip->protonr);
	printf("MTU     : %d\n", dip->mtu);
  }
  syslog(LOG_INFO, "%s connecting %s/%s to local %s/%s  with %s/%d",
	dip->name, 
        dip->remote, inet_ntoa(dip->rmt_ip), 
        dip->local,  inet_ntoa(dip->loc_ip), 
        dip->protocol, dip->mtu);

  sprintf(buff, "-dip (%s)", dip->remote);
  *argp = buff;
  printf("Your IP address is %s ",inet_ntoa(dip->rmt_ip));
  printf("Server address is %s\n",inet_ntoa(dip->loc_ip));
  printf("Netmask is %s MTU is %d  Starting %s\n", 
         dip->netmask, dip->mtu, dip->protocol);
  (void) fflush(stdout);

  /* Initialize this terminal line for 8-bit clean operation. */
  (void) tty_open(NULL);

  /* Make tty the control terminal, detect DCD loss from now. */
#if 0
  if ((i = tty_notlocal()) < 0)
     exit(-1);
#else
  if ((i == tty_login()) < 0)
    exit(-1);
#endif
  
  /* Set up for "no messages". We do not want to be disturbed anymore. */
  (void) tty_nomesg();

  /* Enter background mode here. */
  (void) dip_login_setup(dip);

  exit(0);
}

static int verify_inet_addr(char *p)
{
  int  i    = 0;
  int  dots = 0;
  char ch;

  for (ch = *p++; ch; ch = *p++) {
    switch (ch) {
    case '.':
      dots++;
      if (dots > 3)   return (-1);
      if ((i > 255) || (i < 0)) return (-1);
      i = 0; 
      break;
    case '0':
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
      i = (i * 10) + (int) ch - '0';
      break;
    case '\0':
      goto end_of_loop;
    default:
      return (-1);
    }
  }
  
 end_of_loop:
  if ((dots != 3) || (i > 255) || (i < 0))
    return (-1);
  
  return 0;
}
