/*
 *   config.c
 *
 *   Reads and stores the appropriate information from
 *   the config file specifying the configuration of
 *	(1) this machine
 *	(2) the available domain name servers
 *	(3) the available gateways
 */

#define	CONFIG_DEF	1

/*
 *	Includes
 */
#include <stdio.h>
#include <malloc.h>
#include <string.h>

#include "vtcpip.h"
#include "dfault.h"
#include "evtdef.h"
#include "hstdef.h"
#include "prodef.h"
#include "prodat.h"
#include "tcpdat.h"
#include "bytprc.h"
#include "mapskt.h"
#include "server.h"

#define NUMSPECS 114	/* last CON option for a machine specific option */

/*
 *   States for config file reading state machine.
 *   One for each type of keyword and some for controlling.
 */

#define	CONNAME		101
#define CONHOST		102
#define CONIP		103
#define CONPORT 	104
#define CONNS		105
#define CONGATE		106
#define CONRETR		107
#define CONTO		108
#define CONWIND		109
#define CONSEG		110
#define CONMTU		111
#define CONDELETE	112
#define CONCRMAP	113
#define CONDUP  	114
/*
 *  above this line are per machine entries
 */
#define CONFROM		115
/*
 *  below this line are configuration entries
 */
#define	CONMASK		116
#define CONMYIP		117
#define CONME		118
#define CONDEF  	119
#define CONDOMTO	120
#define CONNDOM		121
#define CONARPTO 	122
#define	CONDTO		123
#define	CONNWIND	124
#define	CONNPKT		125
#define	CONNSEG		126
#define	CONNMTU		127
#define CONSRVC		128
#define	CONTASK		129
#define	CONLOGSESSION	130

char *Smachfile = {"TCP:tcpip.cfg"};

/*
 * Local Variables
 */

struct machinfo *Smachlist,*Smptr;

static	char	Sflags[NUMSPECS-95],	/* which parms we have or have not */
		*Sspace;

static	int	mno,		/* how many machines in host file */
		lineno,		/* line number in hosts file */
		position,	/* position for scanning string */
		constate,	/* state for config file parser */
		inquote;	/* flag, inside quotes now */

static char *Skeyw[] = {
/*==0==*/	"",	
		"name",		/* name of session */
		"host",		/* name of host */
		"hostip",	/* IP number */
		"port",		/* TCP port to go for */
/*==5==*/	"nameserver",	/* name server level */
		"gateway",	/* gateway level */
		"retrans",	/* initial retrans time */
		"contime",	/* timeout for opening connection */
		"mwin",		/* window to allow for this host */
/*==10==*/	"mseg",		/* maximum transfer size(in) */
		"mtu",		/* transfer unit (out) */
		"delete",	/* character for character deletion */
		"crmap",	/* map for Berkeley 4.3 */
		"duplex",	/* half duplex for IBM machines */
/*
 *  above are part of the data structure
 */
/*==15==*/	"copyfrom",	/* copy from another machine */
/*
 *  following are one-time entries
 */
		"netmask",	/* subnetting mask */
		"myip",		/* local machine's IP # */
		"myname",	/* identifying info */
		"domain",	/* default domain for lookup */
/*==20==*/	"nndomto",	/* time-out for DOMAIN */
		"nnretry",	/* # of retries */
		"nnarpto",	/* time-out for ARPs */
		"nndto",	/* time-out for data layer */
		"nnwin",	/* default maximum window */
/*==25==*/	"nnpkt",	/* default maximum packets per transq */
		"nnseg",	/* default maximum transfer size */
		"nnmtu",	/* default maximum transfer unit */
		"service",	/* enable service */
		"task",		/* enable task */
/*==30==*/	"logsession",	/* log TCPIP activity */
		""
	};

/*
 * Convert to Lower Case
 */
VOID strlwr(st)
register char *st;
{
	while(*st) {
		*st = tolower(*st);
		*st++;
	}
}

/*
 *  Sgetspace
 *
 *  get some working space
 */
char *Sgetspace(siz)
register int siz;
{
	register char *s;

	s = (char *) malloc(siz);
	if(s == NULL) {
		Serrline(901);
	}
	return(s);
}

/*
 *  Sreadhosts
 *
 *  read in the hosts file into our in-memory data structure.
 *  Handle everything by keyword, see docs for specifications about file.
 */
int Sreadhosts()
{
	register FILE *fp;
	register int c,retval;

	Smachlist = Smptr = NULL;
	mno = 0;
	/*
	 * state vars
	 */
	position = constate = inquote = lineno = 0;
	/*
	 * open the configuration file
	 */
	if(NULL==(fp = fopen(Smachfile,"r"))) {
		Serrline(900);
		return(1);
	}
	/*
	 * get room for gathering stuff
	 */
	Sspace = Sgetspace(256);
	if(Sspace==NULL) {
		return(1);
	}
	retval = 0;
	while(!retval) {
		c = getc(fp);
		if(c=='#' && !inquote)
			/*
			 * skip to EOL
			 */
			while(c!=EOF && c!='\n' && c!='\r')
				c = getc(fp);
		if(c=='\n' || c=='\r')
			lineno++;
		/*
		 * add character to token
		 */
 		retval = Scontoken(c);
	 }
	fclose(fp);
	free(Sspace);
	/*
	 * make sure name is in list
	 */
	Smadd(-1,"default");
	/*
	 * EOF is normal end
	 */
	if(retval==EOF)
		return(0);
	else
		return(retval);
}

/*
 *  ncstrcmp
 *
 *  No case string compare.
 *  Only returns 0=match, 1=no match, does not compare greater or less
 *  There is a tiny bit of overlap with the | 32 trick, but shouldn't be
 *  a problem.  It causes some different symbols to match.
 */
int ncstrcmp(sa,sb)
register char *sa,*sb;
{
	/*
	 * don't compare leading spaces
	 */
	while(*sa && *sa<33)
		sa++;
	while(*sb && *sb<33)
		sb++;
	while(*sa && *sb) {
		if((*sa!=*sb) && ((*sa | 32)!=(*sb | 32)))
			return(1);
		sa++;
		sb++;
	}
	/*
	 * if both at end of string
	 */
	if(!*sa && !*sb)
		return(0);
	else
		return(1);
}

/*
 *  Serrline
 *
 *  prints the line number of the host file error and posts the event
 *  for the line number error and posts the hosts file error.
 */
VOID Serrline(n)
register int n;
{
	register char *p;

	p = nterrstring(-1);
	sprintf(p,"Config file: error at line %4d",lineno+1);
	ntposterr(-1);
	ntposterr(n);
}

/*
 *  Scontoken
 *
 *  tokenize the strings which get passed to Sconfile.
 *  Handles quotes and uses separators:  <33, ;:=
 */
int Scontoken(c)
register int c;
{
	register int retval;

	if(c==EOF) {
		Sspace[position++] = '\0';
		Sconfile(Sspace);
		/*
		 * make sure last entry gets copied
		 */
		if(!Sflags[0]) {
			if(ncstrcmp("default",Smptr->sname))
				Scopyfrom("default");
			else
				Scopyfrom("==");
		}
		return(-1);
	}
	/*
	 * skip over junk before token
	 */
	if(!position && Sissep(c))
		return(0);
	if(inquote || !Sissep(c)) {
		if(position>200) {
			Serrline(903);
			return(1);
		}
		/*
		 *  check for quotes,
		 * a little mixed up here, could be reorganized
		 */
		if(c=='"' ) {
			/*
			 * beginning of quotes
			 */
			if(!inquote) {
				inquote = 1;
				return(0);
			} else {
				/*
				 * turn off flag and drop through
				 */
				inquote = 0;
			}
		} else {
			/*
			 * check for EOL inside quotes
			 */
			if(c=='\n') {
				Serrline(904);
				return(1);
			}
			/*
			 * include in current string
			 */
			Sspace[position++] = (char)c;
			return(0);
		}
	}
	Sspace[position++] = '\0';
	/*
	 * pass the token along
	 */
	retval = Sconfile(Sspace);
	position = 0;
	inquote = 0;
	Sspace[0] = '\0';
	return(retval);
}

/*
 *  Sconfile
 *
 *  take the characters read from the file and parse them for keywords
 *  which require configuration action.
 */
int Sconfile(s)
register char *s;
{
	register int i;
	int a,b,c,d;
	struct service *sp,*nsp;
	struct tasker *tp,*ntp;


	switch(constate) {
		case 0:		/* lookup keyword */
			if(!(*s))
				/*
				 * empty token
				 */
				return(0);

			/*
			 * search the list for this keyword
			 */
			for(i=1; *Skeyw[i] && ncstrcmp(Skeyw[i],s); i++);

			if(!(*Skeyw[i])) {
				/*
				 * not in list
				 */
				Serrline(902);
				return(0);
			}
			/*
			 * change to state for keyword
			 */
			constate = 100+i;
			/*
			 *  check if this is a machine specific parm
			 *  without a machine to give it to.
			 *  "name" being the only machine specific
			 *  parm allowed, of course
			 */
			if(Smptr==NULL &&
				constate>CONNAME &&
				constate<=NUMSPECS) {
				Serrline(905);
				return(1);
			}
			break;

		case CONNAME:	/* session name */
			/*
			 *  allocate space for upcoming parameters
			 */
			if(Smachlist==NULL) {
				Smachlist = (struct machinfo *)
					Sgetspace(sizeof(struct machinfo));
				if(Smachlist == NULL)
					return(2);
				Smptr = Smachlist;
			} else {
				if(!Sflags[0]) {
					/*
					 * to make sure 'default' gets set
					 */
					if(ncstrcmp("default",Smptr->sname))
						Scopyfrom("default");
					else
						Scopyfrom("==");
				}
				Smptr->next = (struct machinfo *)
					Sgetspace(sizeof(struct machinfo));
				if(Smptr->next == NULL)
					return(2);
				Smptr = Smptr->next;
			}
			Smptr->next = NULL;
			/*
			 * guarantee to be null
			 */
			Smptr->hname = NULL;
			/*
			 * size of name string
			 */
			Smptr->sname = Sgetspace(position);
			if(Smptr->sname == NULL)
				return(2);
			/*
			 * keep name field
			 */
			strcpy(Smptr->sname,s);
			/*
			 * back to new keyword
			 */
			constate = 0;
			/*
			 * we have no parms
			 */
			for(i=0; i<NUMSPECS-99; i++)
				Sflags[i] = 0;
			/*
			 * new machine number
			 */
			Smptr->mno = ++mno;
			break;

		case CONHOST:	/* also a host name */
			Smptr->hname = Sgetspace(position);
			if(Smptr->hname == NULL)
				return(2);
			strcpy(Smptr->hname,s);
			constate = 0;
			/*
			 * set the flag to indicate hostname is found
			 */
			Sflags[CONHOST-100] = 1;
			break;

		case CONIP:	/* IP number for host */
			if(4!=sscanf(s,"%d.%d.%d.%d",&a,&b,&c,&d)) {
				Serrline(906);
				return(3);
			}
			/*
			 * keep number
			 */
			Smptr->hostip[0] = (char)a;
			Smptr->hostip[1] = (char)b;
			Smptr->hostip[2] = (char)c;
			Smptr->hostip[3] = (char)d;
			Smptr->mstat = HFILE;
			constate = 0;
			/*
			 * set the flag to indicate ip number found
			 */
			Sflags[CONIP-100] = 1;
			break;

		case CONPORT:	/* the port number */
			i = atoi(s);
			if(i<1)
				i = 0;
			Smptr->port = i;
			Sflags[CONPORT-100] = 1;
			constate = 0;
			break;

		case CONNS:	/* nameserver level */
			Smptr->nameserv = (char)atoi(s);
			/*
			 * keep NS
			 */
			if(!Sns || (Sns->nameserv>Smptr->nameserv))
				Sns = Smptr;
			constate = 0;
			Sflags[CONNS-100] = 1;
			break;

		case CONGATE:	/* this machine is a gateway */
			/*
			 * gateway level
			 */
			Smptr->gateway = (char)atoi(s);
			constate = 0;
			/*
			 * set the flag for this name being a gateway
			 */
			Sflags[CONGATE-100] = 1;
			break;

		case CONRETR:	/* how long before retransmitting */
			Smptr->retrans = atoi(s) * TICKSPERSEC;
			constate = 0;
			Sflags[CONRETR-100] = 1;
			break;

		case CONTO:	/* time until time out */
			i = atoi(s);
			if(i>2) {
				Smptr->conto = i;
				Sflags[CONTO-100] = 1;
			}
			constate = 0;
			break;

		case CONWIND:	/* transmission window for this host */
			Smptr->window = atoi(s);
			/*
			 * Limit to Machine Defaults
			 */
			if (Smptr->window >nnwin) {
				Smptr->window = nnwin;
			} else
			if (Smptr->window<MINWNDOSIZE) {
				Smptr->window = MINWNDOSIZE;
			}
			constate = 0;
			Sflags[CONWIND-100] = 1;
			break;

		case CONSEG:	/* segment size */
			Smptr->maxseg = atoi(s);
			/*
			 * Limit to Machine Defaults
			 */
			if (Smptr->maxseg>nnseg) {
				Smptr->maxseg = nnseg;
			} else
			if (Smptr->maxseg<MINSEG) {
				Smptr->maxseg = MINSEG;
			}
			constate = 0;
			Sflags[CONSEG-100] = 1;
			break;

		case CONMTU:	/* maximum transmission unit */
			Smptr->mtu = atoi(s);
			/*
			 * Limit to Machine Defaults
			 */
			if (Smptr->mtu>nnmtu) {
				Smptr->mtu = nnmtu;
			} else
			if (Smptr->mtu<TMINSIZE) {
				Smptr->mtu = TMINSIZE;
			}
			constate = 0;
			Sflags[CONMTU-100] = 1;
			break;

		case CONDELETE:	/* character deletion parameter */
			if(!ncstrcmp(s,"backspace"))
				Smptr->delete = 8;
			else
				Smptr->delete = 127;
			constate = 0;
			Sflags[CONDELETE-100] = 1;
			break;

		case CONCRMAP:	/* carriage return mapping */
			if(!ncstrcmp(s,"NONE"))
				Smptr->crmap = 0;
			else
			if(!ncstrcmp(s,"LINEFEED"))
				Smptr->crmap = 10;
			else
			if(!ncstrcmp(s,"4.3BSDCRNUL"))
				Smptr->crmap = BSDCRNUL;
			else
				Smptr->crmap = 0;
			Sflags[CONCRMAP-100] = 1;
			constate = 0;
			break;

		case CONDUP:	/* duplex */
			if(!ncstrcmp(s,"half")) {
				Smptr->halfdup = 1;
				Sflags[CONDUP-100] = 1;
			}
			constate = 0;
			break;

		case CONFROM:	/* copy the rest from another entry */
			Scopyfrom(s);
			Sflags[0] = 1;
			constate = 0;
			break;

		/*
		 *  now the one-time entries
		 *  Generally this information goes
		 *  into the "Scon" structure for later
		 *  retrieval by other routines.
		 */

		case CONME:	/* what my name is */
			strlwr(s);
			strncpy(Scon.me,s,sizeof(Scon.me));
			Scon.me[sizeof(Scon.me)-1] = '\0';
			constate = 0;
			break;

		case CONMYIP:	/* what my ip number is */
			kbprintf("MyIpString=[%s]\r\n",s);
			constate = 0;
			if(!ncstrcmp(s,"rarp")) {
				movebytes(Scon.myipnum,s,4);
				break;
			}
			if(!ncstrcmp(s,"bootp")) {
				movebytes(Scon.myipnum,s,4);
				break;
			}
			if(4!=sscanf(s,"%d.%d.%d.%d",&a,&b,&c,&d)) {
				Serrline(908);
				return(3);
			}
			/*
			 * put number back in s
			 */
			Scon.myipnum[0] = (char)a;
			Scon.myipnum[1] = (char)b;
			Scon.myipnum[2] = (char)c;
			Scon.myipnum[3] = (char)d;
			break;

		case CONMASK:	/* the subnet mask */
			if(4!=sscanf(s,"%d.%d.%d.%d",&a,&b,&c,&d)) {
				Serrline(907);
				return(3);
			}
			Scon.netmask[0] = (char)a;
			Scon.netmask[1] = (char)b;
			Scon.netmask[2] = (char)c;
			Scon.netmask[3] = (char)d;
			Scon.havemask = 1;
			constate = 0;
			break;

		case CONDEF:	/* default domain */
			/*
			 * space for name
			 */
			Scon.defdom = Sgetspace(position);
			if(Scon.defdom == NULL)
				return(2);
			/*
			 * copy it in
			 */
			strcpy(Scon.defdom,s);
			constate = 0;
			break;

		case CONDOMTO:	/* DOMAIN timeout value */
			i = atoi(s);
			if(i>1)
				nndomto = i;
			constate = 0;
			break;

		case CONNDOM:	/* DOMAIN number of retries */
			i = atoi(s);
			if(i>1)
				nnretry = i;
			constate = 0;
			break;

		case CONARPTO:	/* need to lengthen arp time-out (secs) */
			i = atoi(s);
			if(i>0)
				nnarpto = i;
			constate = 0;
			break;

		case CONDTO:	/* Data layer timeout */
			i = atoi(s);
			if(i>0)
				nndto = i;
			constate = 0;
			break;

		case CONNWIND:	/* default maximum window */
			nnwin = atoi(s);
			if (nnwin>MAXWNDOSIZE) {
				nnwin = MAXWNDOSIZE;
			} else
			if (nnwin<MINWNDOSIZE) {
				nnwin = MINWNDOSIZE;
			}
			constate = 0;
			break;

		case CONNPKT:	/* default maximum packet count per transq */
			nnpkt = atoi(s);
			if (nnpkt>MAXPCNT) {
				nnpkt = MAXPCNT;
			} else
			if (nnpkt<MINPCNT) {
				nnpkt = MINPCNT;
			}
			constate = 0;
			break;

		case CONNSEG:	/* default maximum segment size */
			nnseg = atoi(s);
			if (nnseg>MAXSEG) {
				nnseg = MAXSEG;
			} else
			if (nnseg<MINSEG) {
				nnseg = MINSEG;
			}
			constate = 0;
			break;

		case CONNMTU:	/* default maximum transfer unit */
			nnmtu = atoi(s);
			if (nnmtu>TMAXSIZE) {
				nnmtu = TMAXSIZE;
			} else
			if (nnmtu<TMINSIZE) {
				nnmtu = TMINSIZE;
			}
			constate = 0;
			break;

		case CONSRVC:	/* service requests */
			/*
			 *  allocate space for upcoming parameters
			 */
			sp = (struct service *)
					Sgetspace(sizeof(struct service));
			if(sp == NULL)
				return(2);

			/*
			 *  link up services list
			 */
			if(Scon.srvc==NULL) {
				Scon.srvc = sp;
			} else {
				nsp = Scon.srvc;
				while (nsp->next)
					nsp = nsp->next;
				nsp->next = sp;
			}

			/*
			 *  load parameters
			 */
			sp->next = NULL;
			sp->port = 0;
			sp->maxses = 0;
			sp->cmdfil[0] = '\0';

			if(3!=sscanf(s,"%d,%d,%15s",
			&(sp->port), &(sp->maxses), &(sp->cmdfil[0]))) {
				Serrline(909);
				return(3);
			}
				
			constate = 0;
			break;

		case CONTASK:	/* task requests */
			/*
			 *  allocate space for upcoming parameters
			 */
			tp = (struct tasker *)
					Sgetspace(sizeof(struct tasker));
			if(tp == NULL)
				return(2);

			/*
			 *  link up tasker list
			 */
			if(Scon.task==NULL) {
				Scon.task = tp;
			} else {
				ntp = Scon.task;
				while (ntp->next)
					ntp = ntp->next;
				ntp->next = tp;
			}

			/*
			 *  load parameters
			 */
			tp->next = NULL;
			tp->taskid = 0;
			tp->timer = 0;
			tp->jobnum = 0;
			tp->cmdfil[0] = '\0';

			if(3!=sscanf(s,"%d,%d,%15s",
			&(tp->taskid), &(tp->timer), &(tp->cmdfil[0]))) {
				Serrline(909);
				return(3);
			}
				
			constate = 0;
			break;

		case CONLOGSESSION:	/* log sessions */
			tcplog(s);
			constate = 0;
			break;

		default:
			constate = 0;
			break;
	}
	return(0);
}

/*
 *  Scopyfrom
 *
 *  Look at the Sflags array to determine which elements to copy from
 *  a previous machine's entries.  If a machine name has been given as
 *  "default", the state machine will fill in the fields from that
 *  machine's entries.
 *
 *  If the machine name to copyfrom is not present in the list, set the
 *  program default values for each field.
 */
VOID Scopyfrom(s)
register char *s;
{
	register struct machinfo *m;
	register int i;

	/*
	 * search list through list of parms
	 */
	m = Shostlook(-1,s);

	for(i=3; i<=NUMSPECS-100; i++) {
		if(!Sflags[i]) {
		    if(m) {
			/*
			 * copy old value
			 */
			switch(100+i) {
				case CONHOST:
					Smptr->hname = m->hname;
					break;

				case CONIP:
					movebytes(Smptr->hostip,m->hostip,4);
					Smptr->mstat = m->mstat;
					break;

				case CONGATE:
					/*
					 * gateways cannot be copied from
					 */
					Smptr->gateway = 0;
					break;

				case CONNS:
					/*
					 * can't copy nameservers either
					 */
					Smptr->nameserv = 0;
					break;

				case CONRETR:
					Smptr->retrans = m->retrans;
					break;

				case CONWIND:
					Smptr->window = m->window;
					break;

				case CONSEG:
					Smptr->maxseg = m->maxseg;
					break;

				case CONMTU:
					Smptr->mtu = m->mtu;
					break;

				case CONDELETE:
					Smptr->delete = m->delete;
					break;

				case CONCRMAP:
					Smptr->crmap = m->crmap;
					break;

				case CONDUP:
					Smptr->halfdup = m->halfdup;
					break;

				case CONTO:
					Smptr->conto = m->conto;
					break;

				case CONPORT:
					Smptr->port = m->port;
					break;

				default:
					break;
			}
		    } else {
			/*
			 * m=NULL, install default values
			 */
			switch(100+i) {
				case CONHOST:
					Smptr->hname = NULL;
					break;

				case CONIP:
					Smptr->mstat = NOIP;
					break;

				case CONGATE:
					/*
					 * gateways cannot be copied from
					 */
					Smptr->gateway = 0;
					break;

				case CONNS:
					/*
					 * can't copy nameservers either
					 */
					Smptr->nameserv = 0;
					break;

				case CONRETR:
					Smptr->retrans = MINRTO;
					break;

				case CONWIND:
					Smptr->window = DEFWINDOW;
					break;

				case CONSEG:
					Smptr->maxseg = DEFSEG;
						break;
				case CONMTU:
					Smptr->mtu = TSENDSIZE;
					break;

				case CONTO:
					Smptr->conto = CONNWAITTIME;
					break;

				case CONDELETE:
					Smptr->delete = 127;
					break;

				case CONCRMAP:
					Smptr->crmap = 0;
					break;

				case CONDUP:
					Smptr->halfdup = 0;
					break;

				case CONPORT:
					/*
					 * the default port
					 */
					Smptr->port = 0;
					break;

				default:
					break;
			}
		    }
		}
	}
	/*
	 * set that this machine was copied
	 */
	Sflags[0] = 1;
}

/*
 *  Shostfile
 *
 *  if the user wants to change the host file name from 'tcpip.cfg' to
 *  something else.
 */
VOID Shostfile(ptr)
register char *ptr;
{
	/*
	 *  note that the area with the file name must stay allocated for
	 *  later reference, typically it is in some argv[] parm.
	 */
	Smachfile = ptr;	
}

/*
 *  Sissep
 *
 *  is the character a valid separator for the hosts file?
 *  separators are white space, special chars and =
 */
int Sissep(c)
register int c;
{
	if(c<33 || c=='=')
		return(1);
	return(0);
}

/*
 *  Smadd
 *
 *  if(sknum<0)
 *  If machine is there just returns pointer, else
 *  Add a machine to the list. Increments machine number of machine.
 *  Puts in parameters copied from the "default" entry.
 *
 *  If(sknum>=0)
 *  If machine is there copy to socket(sknum),
 *  else put in parameters copied from the "default" entry.
 */
struct machinfo *Smadd(sknum,mname)
int sknum;
register char *mname;
{
	int i;
	register struct machinfo *m;
	register struct socket *skt;

	if(sknum>=0)
		skt = (struct socket *) mapskt(sknum);

	/*
	 *  First, do we have the name already?
	 */
	m = Shostlook(sknum,mname);
	if(m)
		if(sknum<0) {
			return(m);
		} else {
			skt->mpp = &skt->mach;
			movebytes(skt->mpp,m,sizeof(struct machinfo));
			skt->mach.port = skt->sktport;
			return(skt->mpp);
		}
	/*
	 *   Don't have name, add another record
	 */
	if(sknum<0) {
		Smptr = (struct machinfo *) malloc(sizeof(struct machinfo));
		if(Smptr==NULL)
			return(NULL);
	} else {
		Smptr = skt->mpp = &skt->mach;
	}
	/*
	 * we have no parms
	 */
	for(i=0; i<NUMSPECS-99; i++)
		Sflags[i] = 0;
	Scopyfrom("default");
	Smptr->sname = NULL;
	if(sknum<0) {
		/*
		 * internal machine list
		 */
		Smptr->hname = malloc(strlen(mname)+1);
		Smptr->mno = ++mno;
		Smptr->next = Smachlist;
		Smachlist = Smptr;
	} else {
		/*
		 * socket machine data
		 */
		Smptr->hname = &skt->hname;
		Smptr->mno = sknum;
		Smptr->next = NULL;
		skt->mach.port = skt->sktport;
	}
	/*
	 * copy in name of machine
	 */
	if(Smptr->hname)
		strcpy(Smptr->hname,mname);
	/*
	 * No IP yet
	 */
	Smptr->mstat = NOIP;

	return(Smptr);
}

/*
 *  Sgethost
 *
 *  get host by name
 *  Given the name of a host machine, search our database to see if we
 *  have that host ip number.  Search first the name field, and then the
 *  hostname field.  If the IP # is given, returns a ptr to the
 *  default machine record with that IP # installed.
 *  Returns the pointer to a valid record, or NULL if the IP # cannot
 *  be deciphered.
 */
struct machinfo *Sgethost(sknum,machine)
int sknum;
register char *machine;
{
	int i,j,k,l;
	char ipto[4],myipnum[4],xmask[4];
	long hnum;
	register struct socket *skt;
	register struct machinfo *mhost;

	mhost = NULL;
	/*
	 *  First, check for the '.' character which means we should
	 *  use the current netmask to build an IP number for the local
	 *  network. Take the host number, install it in the ipto[] array.
	 *  Then mask in my IP number's network portion to build the final
	 *  IP address.
	 */
	if(machine[0]=='.') {
		/*
		 * on my local network
		 */
		ntgtip(myipnum);
		/*
		 * mask of network portion of IP #
		 */
		ntgtmask(xmask);
		/*
		 * host number for local network
		 */
		sscanf(&machine[1],"%ld",&hnum);
		for(i=3; i>=0; i--) {
			if(xmask[i]) {
				ipto[i] = myipnum[i];
			} else {
				ipto[i] = hnum & 0xFFL;
				hnum >>= 8;
			}
		}
	} else
	/*
	 *  next, is it an IP number?  We take it if the number is in four
	 *  parts, separated by periods.
	 */
	if(4==sscanf(machine,"%d.%d.%d.%d",&i,&j,&k,&l)) {
		/*
		 * given ip num
		 */
		ipto[0] = (char)i;
		ipto[1] = (char)j;
		ipto[2] = (char)k;
		ipto[3] = (char)l;
	} else {
	/*
	 *  lastly, it must be a name, first check the local host table
	 *  A first number of 127 means that it doesn't have an IP number, but
	 *  is in the table(strange occurrence)
	 */
		/*
		 * look it up
		 */
		mhost = Shostlook(sknum,machine);
		if(mhost==NULL) {
			ntposterr(805);
			return(NULL);
		}
		if(mhost->mstat<HAVEIP) {
			ntposterr(806);
			return(NULL);
		}
	}
	skt = (struct socket *) mapskt(sknum);
	skt->mpp = &skt->mach;
	if(mhost) {
		movebytes(&skt->mach,mhost,sizeof(struct machinfo));
	} else {
		mhost = Shostlook(-1,"default");
		/*
		 * set up internal parameters after loading defaults
		 */
		movebytes(&skt->mach,mhost,sizeof(struct machinfo));
		movebytes(&skt->mach.hostip,ipto,4);
		skt->mach.mstat = HAVEIP;
	}
	/*
	 * load names
	 */
	skt->mach.sname = skt->sname;
	if(skt->sname[0]=='\0')
		strncpy(skt->sname,mhost->sname,sizeof(skt->sname)-1);
	skt->mach.hname = skt->hname;
	if(skt->hname[0]=='\0')
		strncpy(skt->hname,mhost->hname,sizeof(skt->hname)-1);
	/*
	 * port number
	 */
	skt->mach.port = skt->sktport;

	return(&skt->mach);
}

/*
 *  Shostlook
 *
 *  The straightforward list searcher.  Looks for either the
 *  session name matching or the host name matching.  NULL if neither.
 */
struct machinfo *Shostlook(sknum,hname)
int sknum;
register char *hname;
{
	register struct machinfo *m;
	register struct socket *skt;

	/*
	 * Check socket machine record
	 */
	if(sknum>=0) {
		skt = (struct socket *) mapskt(sknum);
		m = skt->mpp;
		if(m) {
			if(m->sname && !ncstrcmp(hname,m->sname))
				return(m);
			if(m->hname && !ncstrcmp(hname,m->hname))
				return(m);
		}
	}
	/*
	 * Check Internal machine list
	 */
	m = Smachlist;
	while(m!=NULL) {
		if(m->sname && !ncstrcmp(hname,m->sname))
			return(m);
		if(m->hname && !ncstrcmp(hname,m->hname))
			return(m);
		m = m->next;
	}
	return(NULL);
}

/*
 *  Snewns
 *
 *  Rotate to the next nameserver
 *  Chooses the next highest number from the nameserv field
 */
int Snewns()
{
	register struct machinfo *m,*low;
	register int i;

	/*
	 * safety, should never happen
	 */
	if(!Sns)
		Sns = Smachlist;
	low = Sns;
	/*
	 * what is value now?
	 */
	i = Sns->nameserv;
	m = Smachlist;
	while(m) {
		if(m->nameserv==(char)i+1) {
			Sns = m;
			return(0);
		}
		if((m->nameserv>0) && (m->nameserv<low->nameserv))
			low = m;
		m = m->next;
	}
	if(Sns==low)
		/*
		 * no alternate
		 */
		return(1);
	else
		Sns = low;
	return(0);
}

/*
 *  Ssetgates
 *
 *  set up the gateway machines and the subnet mask
 */
VOID Ssetgates()
{
	register struct machinfo *m;
	register int level,again;

	/*
	 * leave default unless specified
	 */
	if(Scon.havemask) {
		ntstmask(Scon.netmask);
	}
	/*
	 *  Search the list of machines for gateway flags.
	 *  Invoke ntstgate in increasing order of gateway level #s.
	 *  Terminates when it gets through list without
	 *  finding next higher number.
	 */
	level = 0;
	do {
		level++;
		again = 0;
		m = Smachlist;
		while(m!=NULL) {
			if(m->gateway ==
				(char)level && m->mstat>=HAVEIP) {
				ntstgate(m->hostip);
			}
			if(m->gateway==(char)level+1)
				again = 1;
			m = m->next;
		}
	} while(again);
}
                                                          