4.4BSD/usr/src/old/berknet/netdaemon.c

Compare this file to the similar file:
Show the results in this format:

static char sccsid[] = "@(#)netdaemon.c	4.5	(Berkeley)	10/13/82";

/* sccs id variable */
static char *netdaemon_sid = "@(#)netdaemon.c	1.10";

/*

	The daemon program that runs the network.

Usage:
	netdaemon -m mach [-r readfd] [-w writefd] [-d] [-h]
		[-os] [-or] [-ou num] [-p len] [-8] [-l]

Must be started by root.
Options:
	-d		turn debugging on
	-h		use high-speed link (not implemented yet)
	-l		don't use net line discipline, even if available
	-m mach		remote machine is mach (required)
	-os		only send
	-or		only receive
	-ou num		only send things with uid = num
	-p num		length of packet
	-r num		if simulute w/pipes, read from num
	-w num		if simulate w/pipes, write on num
*/

# include "defs.h"
/* take a time, adjust to be in PST, and divide by no of secs in a day */
/* adjust by 10 mins, and day is considered to begin at 3AM */
/* (6*3600 = 21600) + 17400 = 39000 */
/* number of seconds in a day, usually 86400L */
# define nsecday 86400L
/* number of days since time began */
# define numdays(S) ((S - 39000L)/nsecday)
/* set my priority to normal */
# define RENICE0() { if (getuid() == 0) { nice(-40); nice(20); nice(0); } }

/* global variables */
extern char **environ;
struct dumpstruc dump;
struct bstruct btable[];
struct daemonparms netd;
struct userinfo status;

/* local variables */
static long length;
static DIR *dir;
/* static char sheader[] = 		"ABCDE"; */
static char tempfile[]= 	TEMPFILE;
static char publogfile[]=  	PUBLOGFILE;
static struct stat statbuf;
int handlekill();
static char frommach;
long linechars();

main(argc,argv)
  char **argv; {
	register int i;
	long ltime,t;
	char buf[100];

	nice(-1);
	if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
		signal(SIGHUP, handlekill);
	if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
		signal(SIGQUIT, handlekill);
	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
		signal(SIGINT, handlekill);
	if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
		signal(SIGTERM, handlekill);
	debugflg = DBV;
	setupdaemon(argc,argv);
	/* now running alone as a daemon */
		/*
		for(i=0; i<15; i++)close(i);
		signal(SIGHUP,SIG_IGN);
		signal(SIGQUIT,SIG_IGN);
		signal(SIGINT,SIG_IGN);
		*/
	/* set the umask to a reasonable value */
	umask( 022 );
	senddir[strlen(senddir)-1] = remote;		/* choose dir */
	if(chdir(senddir) < 0){
		perror(senddir);
		exit(EX_OSFILE);
		}
	dir = opendir(senddir);
	if(dir == NULL){
		perror(senddir);
		exit(EX_OSFILE);
		}
	mktemp(tempfile);
	tempfile[strlen(tempfile) - 7] = remote;
	ltime = gettime();
	if(ltime == 0L)
		fprintf(stderr,"The network says 'The clock is set wrong.'\n");
	sprintf(buf,"net restarted to %s %d %s",longname(remote),
		getpid(),ctime(&ltime));
	dump.longtime = ltime;
	dump.lastndays = numdays(ltime);
	addtolog(remote,buf);
	addtopublic(buf);
	fprintf(stderr,buf);
	if(!debugflg)fclose(stderr);
	sendpurge();
	mainloop();
	/* never returns */
}
/* the main loop of the daemon, alternatively rcv then send, if poss.*/
mainloop(){
	register int i;

	for(;;){	/* begin reading file */
		debug("daemon %c %d\n",remote,getpid());
		/* first receive */
		if(netd.dp_sndorcv >= 0){	/* if we can receive */
			i = netrcv();
			if(i == -1)dump.nabnormal++;
		}
		/* now look to send */
		if(netd.dp_sndorcv <= 0)	/* if we can send */
			netsend();
		/* print out statistics if the right time */
		printstat();
		dump.nloop++;
	}
}
	/* this code is a little strange because some machines
	   seem to have trouble having the date set, and time()
	   returns 0 until somebody remembers to set the date */
printstat(){
	long thisndays, thistime;
	thistime = gettime();
	thisndays = numdays(thistime);
	if(dump.longtime == 0L){
		dump.longtime = thistime;
		dump.lastndays = thisndays;
		return;
		}
	if(thisndays == dump.lastndays + 1L) dumpit(thistime);
	dump.lastndays = thisndays;
}
/* look for files to send */
netsend(){
	static long lasttime = 0;
	static char nleft = 1;
	long lFileLen,diff;
	double drate;
	register int uid,uidBest;
	char *sdate,*sn,*swait;
	long ot,nt,filesize;
	register int i;
	char stemp[20];
	static char jname[FNS];
	register struct direct *dp;

	debug("ck send");
	if(stat(senddir,&statbuf) < 0){
		error("%s %s",senddir,sys_errlist[errno]);
		return;
		}
	if(statbuf.st_mtime == lasttime && nleft == 0)return;	/* no need to search */
	lasttime = statbuf.st_mtime;
	rewinddir(dir);
	lFileLen = 10000000L;
	nleft = 0;
	while((dp = readdir(dir)) != NULL){
		if(dp->d_name[0] != 'c'
		   || dp->d_name[1] != 'f'
		   || dp->d_name[2] != remote
		   || stat(dp->d_name,&statbuf) < 0
		   || statbuf.st_mode == 0)
			continue;
		dp->d_name[0] = 'd';
		if(stat(dp->d_name,&statbuf) < 0 || statbuf.st_mode == 0)
			continue;
		uid = guid(statbuf.st_uid,statbuf.st_gid);
		if(netd.dp_onlyuid != 0 && uid != netd.dp_onlyuid && uid != SUPERUSER
			&& uid != NUID)continue;
		nleft++;
		filesize = getsize(&statbuf);
#ifndef DONTHOLDBIG
		if( (filesize > MAXDAYFILE) && day() ) {
			if( !debugflg )
				continue;
			else
				debug("sending large file %s\n", dp->d_name );
		}
#endif DONTHOLDBIG
		if(lFileLen > filesize){
			lFileLen = filesize;
			strcpy(jname,dp->d_name);
			uidBest = uid;
		}
# ifdef MAXSENDQ
		if(nleft > MAXSENDQ)break;
# endif MAXSENDQ
	}
	if(lFileLen == 10000000L)return;
	strcpy(stemp,jname);
	stemp[0] = 'c';
	sn = SnFromUid(uidBest);
	if(sn == NULL){
		addtolog(remote,"Unknown userid %d\n",uidBest);
		addtolog(remote,"Removing %s\n",stemp);
		unlink(stemp);
		return;
	}
	addtolog(remote,"^S %s %c: %s ",sn,remote,jname+2);
	ot = gettime();
	if(send(jname) == 0)return;
	nt = gettime();
	filesize = getsize(&statbuf);
	unlink(jname);
	unlink(stemp);
	diff = nt - ot;
	if(diff < 1)diff = 1;		/* avoid dividing by zero */
	sdate = ctime(&nt)+4;
	sdate[strlen(sdate) -9] = 0;
	swait = comptime(ot - statbuf.st_mtime);
	jname[3] = jname[2];
# ifndef NOFP
	drate = (double)filesize / (double)diff;
	addtolog(remote,"^T%c(%s, %ldb, %ldsec, %4.1fb/sec, w %s)\n",
		remote,sdate,filesize, diff,drate, swait);
# else NOFP
	addtolog(remote,"^T%c(%s, %ldb, %ldsec, w %s)\n",
		remote,sdate,filesize, diff,swait);
# endif NOFP
	addtopublic("%s: sent %-8s to %s (%s, %ld b, wait %s)\n",
		sdate,sn,longname(remote),jname+3,filesize,swait);
	dump.nsend++;
	dump.bytetot += filesize;
	dump.elaptot += diff;
	}

/*
   day() returns 1 if the time is between 6AM and 12PM
*/
day()
{
	int hour;
	long t;
	char *ctime();

	time( &t );
	sscanf( ctime( &t ), "%*s%*s%*s%2d", &hour );
	if( (hour>=0) && (hour<6) )
		return( 0 );		/* night */
	else
		return( 1 );		/* day */
}

send(jname)
	char *jname;
{	/* push those bytes */
	/* returns 0 if send fails, 1 otherwise */
	register int n;
	int i;
	long lsize;
	char mbuf[20], buf[MAXNBUF];
	register char *p;
	register FILE *jfile;

	debug("send %s",jname);
	if(stat(jname,&statbuf) < 0)goto sfail;
	lsize = getsize(&statbuf);
	if(lsize < MINSIZE){		/* all files are at least this long */
		unlink(jname);
		jname[0] = 'c';
		unlink(jname);
		return(1);
		}
	jfile = fopen(jname,"r");
	if(jfile == NULL)goto sfail;
	/*
	strcpy(mbuf,sheader);
	i = strlen(sheader);
	p = (char *)&lsize;
	lsize = fixuplong(lsize);
	mbuf[i] = *p++;
	mbuf[i+1] = *p++;
	mbuf[i+2] = *p++;
	mbuf[i+3] = *p++;
	i = i + 4;
	sendreset();
	*/
	initseqno();
	sprintf(mbuf,"|%08ld|",lsize);
	i = 10;
	if(xwrite(mbuf,i) == WRITEFAIL)goto bwrite;
	while((n=read(fileno(jfile),buf,MAXNBUF)) > 0)
		if(xwrite(buf,n) == WRITEFAIL)goto bwrite;
	fclose(jfile);
	debug("end send");
	return(1);
bwrite:
	dump.nsendfail++;
	fclose(jfile);
	addtolog(remote,"^F%c\n",remote);
	return(0);
sfail:
	error("%s: %s",jname,sys_errlist[errno]);
	dump.nsendfail++;
	return(0);
	}
netrcv(){
	/* returns -2 in normal fail, -1 in abnormal fail, >= 0 otherwise */
	char sin;
	char mgetc(), *s;
	register int n;
	char c;
	int i, dummy, pid;
	unsigned rcode;
	long otime,olength,diff,rcvfinish,nt;
	double r;
	char hbuf[20], buf[MAXNBUF];
	register FILE *temp;
	static struct header hd;

	initseqno();
	/*
	n = nread(hbuf,strlen(sheader));
	if(n == BROKENREAD)return(-2);
	if(n != strlen(sheader) || strcmp(sheader,hbuf) != 0){
		error("wrong head %d %s",n,hbuf);
		return(-1);
		}
	n = nread(&length,4);
	length = fixuplong(length);
	*/
	n = nread(hbuf,10);
	if(n == BROKENREAD)return(-2);
	if(n != 10){
		error("bad length nread %d",n);
		return(-1);
		}
	hbuf[10] = 0;
	if(hbuf[0] != '|' || hbuf[9] != '|'){
		error("poor format %s",hbuf);
		return(-1);
		}
	hbuf[9] = 0;
	length = atol(hbuf+1);
	if(length < 0 || length > 100000000L){
		error("bad length %ld",length);
		return(-1);
		}
	dump.braw = 4;
	olength = length;
	otime = gettime();
	debug("length = %ld\n",length);

/* 
	begin parsing header

	from local to remote (requests)
	code	net option	reason
	q			normal request
	y	-y		simply skips login check (used by netlpr)

	from remote to local
	code	net option	reason
	w	-w		message to be written/mailed back
	s	-z		normal response
*/

	i = readhd(&hd);
	if(i == -3)goto forw;			/* being forwarded thru us */
	if(i != 0)return(i);

	strcpy(status.login, hd.hd_snto);
	strcpy(status.localname,hd.hd_snfrom);

	demask(hd.hd_spasswd);

	s = hd.hd_scmdvirt;
	while(*s && *s != ' ')s++;
	c = *s;
	*s = 0;
	if(strcmp(hd.hd_scmdvirt,"netlpr") == 0)dump.nnetlpr++;
	else if(strcmp(hd.hd_scmdvirt,"netmail") == 0)dump.nnetmail++;
	else if(strcmp(hd.hd_scmdvirt,"mail") == 0)dump.nsmail++;
	else if(strcmp(hd.hd_scmdvirt,"netcp") == 0)dump.nnetcp++;
	else if(strcmp(hd.hd_scmdvirt,"response") == 0)dump.nresp++;
	else dump.nnet++;
	*s = c;

	printhd(&hd);

	/* any chars left are data */
forw:
	sin = 0;
	if(length > 0){	/* make a temp input file */
		increment(tempfile);
		temp = fopen(tempfile,"w");
		if(temp == NULL){
			error("%s %s",tempfile,sys_errlist[errno]);
			return(-1);
			}
		chmod(tempfile,0600);
		if(hd.hd_mchto != local){
			fprintf(temp,"%c :%c :",hd.hd_code,hd.hd_mchto);
			fflush(temp);
		}
		/* this is the loop to read in all the data */
		while((n = mread(buf,MAXNBUF)) > 0)
			if(write(fileno(temp),buf,n) != n){
				error("%s %s",tempfile,sys_errlist[errno]);
				fclose(temp);
				unlink(tempfile);
				return(-1);
				};
		fclose(temp);
		if(n == BROKENREAD || length > 0){
			unlink(tempfile);
			return(-2);
			}
		sin = 1;
		if(hd.hd_mchto != local){
			diff = gettime() - otime;
			if(diff < 1)diff = 1;	/* avoid dividing by 0 */
# ifndef NOFP
			r = olength;
			r = r/diff;
			addtolog(remote,"^P(to %c, %ldb, %ldsec, %4.1fb/sec)\n",
				hd.hd_mchto,olength,diff,r);
# else NOFP
			addtolog(remote,"^P(to %c, %ldb, %ldsec)\n",
				hd.hd_mchto,olength,diff);
# endif NOFP
			dump.npass++;
			dump.bytetot += olength;
			dump.elaptot += diff;
			while((pid = fork()) == -1)sleep(2);
			if(pid == 0){
				RENICE0();
#ifdef CCV7
				/* make sure the spawned child has it's own
					group process to avoid the nasty
					"try again" message
				*/
				setpgrp();
#endif CCV7
				execl(netcmd,"net","-x","-m",longname(hd.hd_mchto),
					"-s",tempfile,0);
				error("%s: %s",netcmd,sys_errlist[errno]);
				exit(EX_UNAVAILABLE);
				}
			wait(&rcode);
			unlink(tempfile);
			rcode >>= 8;
			if(rcode != 0)
				error("pass-thru rcode %d", rcode);
			debug("passthru to %c code %c rcode %d",
				hd.hd_mchto,hd.hd_code,rcode);
			return(1);
			}
		}
	if(length > 0){error("file too short"); return(-1); }
	rcvfinish = gettime();

	while((pid = fork()) == -1)sleep(2);
	if(pid > 0){
		wait(&dummy);
		return(1);	/* normal return */
	}
	/* this is a child, who will go ahead and execute the command */
	/* running uid=0 at this point */
	RENICE0();
	/* nice(0 set back to 0 */
#ifdef CCV7
	/* separate group process */
	setpgrp();
#endif CCV7

	while((pid = fork()) == -1)sleep(2);
	if(pid != 0)exit(EX_OK);

	/* child process which forks and waits */
	mktemp(resfile);
	while((pid = fork()) == -1)sleep(2);
	if(pid == 0){
		/* child */
		strcpy(status.loginshell,Bsh);
		frommach = hd.hd_mchfrom;
		n = check(&hd,(hd.hd_code == 'q'));
		if(!n)errormsg(TRUE,&hd,NULL,
			"Bad remote login/password '%s'",hd.hd_snto);
		temp = fopen(resfile,"w");
		if(temp == NULL)
			errormsg(TRUE,&hd,NULL,
			"Create file %s: %s",resfile,sys_errlist[errno]);
		fclose(temp);
		chmod(resfile,0600);
		mchown(resfile,status.muid,status.mgid);
		if(sin)
			mchown(tempfile,status.muid,status.mgid);
		else tempfile[0] = 0;
		setgid(status.mgid);
		setuid(status.muid);
		/* after this point our gid, uid is the target user's */
		excmd(&hd,resfile,tempfile);
	}
	/* parent */
	wait(&rcode);
	rcode = (((rcode&077400) >>8) &0177);
	/*
	fclose(stdin);
	fclose(stdout);
	fclose(stderr);
	*/
	if(sin)unlink(tempfile);
	/* 
	   now send something back to the sender 
	   unless this was a response (file or message)
	*/
	if((hd.hd_code == 'q' || hd.hd_code == 'y')
	&& (hd.hd_srespfile[0] || !hd.hd_fnonotify))
		sndresponse(&hd,rcode);
	unlink(resfile);
	s = ctime(&rcvfinish);
	s += 4;
	s[strlen(s) -8] = 0;
	diff = rcvfinish - otime;
	if(diff < 1)diff = 1;		/* avoid dividing by zero */
	dump.bytetot += olength;
	dump.elaptot += diff;
	sprintf(buf,"%s rcv  %c:%-8s (%s)",
		s,hd.hd_mchfrom,hd.hd_snfrom,hd.hd_snto);
	addtolog(remote,"%s C: %s\n",buf,hd.hd_scmdvirt);
	addtopublic("%s R: %d C: %s\n",buf,rcode,hd.hd_scmdvirt);
	nt = rcvfinish - hd.hd_ltimesent;
	buf[0] = 0;
	if(nt > 0L)sprintf(buf," took (%s)",comptime(nt));
# ifndef NOFP
	r = olength;
	r = r/diff;
	addtolog(remote,"\t\tR: %d%s %ldb %ldsec %4.1fb/sec\n",
		rcode,buf,olength,diff,r);
	r = dump.braw;
	r = r/diff;
	addtolog(remote,"\t\t%4.1frb/sec %4.1f%% use\n",r,(r/linechars())*100L);
# else NOFP
	addtolog(remote,"\t\tR: %d%s %ldb %ldsec\n",
		rcode,buf,olength,diff);
# endif NOFP
	exit(EX_OK);
	/*UNREACHED*/
	}
long linechars(){
	if(netd.dp_inspeed == 13)return(960L);
	else return(120L);
	}
/* 
	execute the user's command
	this procedure is executed with uid, gid of the user
*/
excmd(phd,tempresfile,tempinfile)
	register struct header *phd;
	char *tempresfile, *tempinfile;
{
	FILE *fd;
	int i, uid;
	register char *s, c;

	uid = getuid();
	uid = uidmask(uid);
	status.muid = uidmask(status.muid);
	if(uid != status.muid)error("setuid fails");
	debug("uid: %u, gid: %u\n",uid,status.mgid);
	/* check for allowed root commands, for security reasons */
	if(uid == SUPERUSER){
		s = phd->hd_scmdact;
		while(*s && *s != ' ')s++;
		c = *s;
		*s = 0;
		/* these are the only commands root may execute */
		if(strcmp(phd->hd_scmdact,"cat")            	!= 0
		&& strcmp(phd->hd_scmdact,MWRITECMD)        	!= 0
		&& strcmp(phd->hd_scmdact,"/bin/cat")       	!= 0
		&& strcmp(phd->hd_scmdact,"netrm")          	!= 0
		&& strcmp(phd->hd_scmdact,"/usr/lib/tq")    	!= 0
		&& strcmp(phd->hd_scmdact,"/usr/cc/lib/tq") 	!= 0
		&& strcmp(phd->hd_scmdact,"/usr/lib/rtrrm") 	!= 0
		&& strcmp(phd->hd_scmdact,"/usr/cc/lib/rtrrm")	!= 0
		&& strcmp(phd->hd_scmdact,"lpr")            	!= 0)
			errormsg(TRUE,phd,tempresfile,
				"Not allowed to execute '%s' as root",
				phd->hd_scmdact);
		*s = c;
		}
	if(chdir(status.dir) < 0)
		errormsg(TRUE,phd,tempresfile,
			"chdir %s: %s",status.dir,sys_errlist[errno]);
	setenv(status.dir);	/* set up v7 environment */
	if(tempinfile[0])mreopen(TRUE,phd,tempresfile,tempinfile,"r",stdin);
	else if(phd->hd_sinfile[0])mreopen(TRUE,phd,tempresfile,phd->hd_sinfile,"r",stdin);
	else mreopen(TRUE,phd,tempresfile,"/dev/null","r",stdin);
	if(phd->hd_code == 's' && phd->hd_soutfile[0]){
		if(stat(phd->hd_soutfile,&statbuf) < 0
		   || getsize(&statbuf) != 0)
			errormsg(FALSE,phd,tempresfile,"Bad result file '%s'",phd->hd_soutfile);
		mreopen(TRUE,phd,tempresfile,phd->hd_soutfile,"w",stdout);
		}
	else if(phd->hd_soutfile[0]){
		fd = fopen(phd->hd_soutfile,"w");
		if(fd == NULL)
			errormsg(TRUE,phd,tempresfile,"Open file %s: %s",
				phd->hd_soutfile,sys_errlist[errno]);
		fclose(fd);
		mreopen(TRUE,phd,tempresfile,phd->hd_soutfile,"w",stdout);
		}
	else mreopen(TRUE,phd,tempresfile,tempresfile,"a",stdout);
	debug("exec '%s'\n",phd->hd_scmdact);
	if(debugflg == 0){
		/* cheat */
		close(2);
		dup(1);
		/*
		mreopen(TRUE,phd,tempresfile,tempresfile,"a",stderr);
		*/
		}
	for(i=3;i<15;i++)close(i);
	if(strcmp(phd->hd_scmdact,"cat") == 0
	|| strcmp(phd->hd_scmdact,"/bin/cat") == 0)excat();
	do {
		mexecl(status.loginshell,"sh","-c",phd->hd_scmdact,0);
		sleep(2);
		} while(errno == ETXTBSY);
	perror(status.loginshell);
	exit(EX_UNAVAILABLE);
}
/* 
	send back a response

	if errormsg was called the resfile should be unlinked,
	to avoid two messages being sent there
*/
sndresponse(phd,rcode)
unsigned rcode;
struct header *phd;
{
	char cmdstr[BUFSIZ], buf[BUFSIZ];
	int dummy;
	long maxfile = MAXFILELARGE;
	/* send response back if a response file
	was given or if mail/write is allowed */
	if(stat(resfile,&statbuf) < 0){
		error("%s %s",resfile,sys_errlist[errno]);
		return;
		}
	if(getsize(&statbuf) >= maxfile){
		errormsg(TRUE,phd,"Result file too large - not sent");
		return;
		}
	if(getsize(&statbuf) == 0){
		/* response file specified, no output generated */
		if(phd->hd_srespfile[0] != 0)return;
		/* quiet option - no output and a rcode of 0 */
		if(rcode == 0 && phd->hd_fquiet)return;
	}
	/* use both old and new mwrite parm lists */

	if(phd->hd_srespfile[0])
		sprintf(cmdstr,"-o %s cat",phd->hd_srespfile);
	else sprintf(cmdstr,
"%s %s %s %lo %c %s \"'%s'\" %ld -t %s -f %s -x %ld -c \"'%s'\" -y %s -e %ld -r %d",
	MWRITECMD, phd->hd_snfrom,phd->hd_sttyname,phd->hd_lttytime,
	phd->hd_mchto,phd->hd_snto, phd->hd_scmdvirt,phd->hd_ltimesent-TIMEBASE,
	phd->hd_addrfrom, phd->hd_addrto, phd->hd_lttytime,
	phd->hd_scmdvirt, phd->hd_sttyname, phd->hd_ltimesent-TIMEBASE, rcode);

	sprintf(buf,"%s -m%c -z -b -l %s -s %s -c response %s",
		netcmd,phd->hd_mchfrom,phd->hd_snfrom,resfile,cmdstr);
	dummy = system(buf);		/* execute command buf */
}
	
/*

	excat
	does nothing more than copy standard input to standard
	output, like the cat command, but reports write errors.
	Uses getc and putc rather than fwrite and fread because
	the latter call getc and putc.
*/
excat(){
	register int n;
	char buf[BUFSIZ];

	errno = 0;
	while((n = read(0,buf,BUFSIZ)) > 0){
		if(write(1,buf,n) != n){
			perror("filecat: stdout");
			exit(EX_OSFILE);
			}
		}
	if(errno){
		perror("filecat: stdin");
		exit(EX_OSFILE);
	}
	exit(EX_OK);
}
/* returns errors for netrcv() */
static readhd(phd)
register struct header *phd;
{
	char cflag, sbuf[BUFSIZ], parmlist[PARMLIST], *cptr;
	int i, code;
	code = mgetc();
	phd->hd_mchto = mgetc();
	if(code != 'q' && code != 'y' && code != 'w' && code != 's'){
		error("bad code");
		return(-1);
		}
	phd->hd_code = code;
	for(i = 0; i < MAXINX; i++)
		if(phd->hd_mchto == inxtoch(i)) break;
	if(i >= MAXINX){
		error("bad phd->hd_mchto");
		return(-1);
		}
	if(phd->hd_mchto != local)return(-3);	/* being forwarded through us */
	phd->hd_mchfrom = mgetc();
	phd->hd_vmajor = mgetc();
	phd->hd_vminor = mgetc();
	i = 0;
	i += mgets(phd->hd_snto,NS);
	i += mgets(phd->hd_spasswd,20);
	i += mgets(phd->hd_sinfile,FNS);
	i += mgets(phd->hd_soutfile,FNS);
	i += mgets(phd->hd_srespfile,FNS);
	i += mgets(phd->hd_snfrom,NS);

	/* addrfrom is the person who sent this to us,
	   addrto is the person who received the command, i.e.
	   addrto is on this machine */
	if(phd->hd_snfrom[0] == 0)strcpy(phd->hd_snfrom,"root");
	sprintf(phd->hd_addrfrom,  "%s:%s",longname(phd->hd_mchfrom),phd->hd_snfrom);
	sprintf(phd->hd_addrto,    "%s:%s",longname(phd->hd_mchto),phd->hd_snto);

	i += mgets(phd->hd_sttyname,20);
	if(phd->hd_sttyname[0] == 0)strcpy(phd->hd_sttyname,"/dev/ttyx");
	cflag = mgetc();
	if(!phd->hd_mchfrom || !phd->hd_code || !cflag || !phd->hd_vmajor || !phd->hd_vminor){
		error("mgetc fails");
		return(-1);
		}

	cflag -= 'a';
	phd->hd_fnonotify = (cflag & F_NONOTIFY);
	phd->hd_fquiet = (cflag & F_QUIET);

	phd->hd_vmajor -= 'a';
	phd->hd_vminor -= 'a';

	i += mgets(sbuf,BUFSIZ);
	phd->hd_lttytime = 0;
	sscanf(sbuf,"%lo",&phd->hd_lttytime);

	i += mgets(parmlist,PARMLIST);
#ifdef CRN
	cptr = parmlist;
	while( *cptr != '(' )
		cptr++;
	*cptr = '\0';
	strcpy( phd->hd_ijobno, parmlist );
	*cptr = '(';
#else CRN
	strcpy( phd->hd_ijobno, "XYZZ" );
#endif CRN
	/* keep variable parameter list in crn slot */
	parseparmlist(parmlist);

	i += mgets(sbuf,BUFSIZ);		/* time sent */
	sscanf(sbuf,"%ld",&phd->hd_ltimesent);
	phd->hd_ltimesent += TIMEBASE;
	i += mgetcmd(phd->hd_scmdact);
	i += mgetcmd(phd->hd_scmdvirt);
	if(i != 0){error("mgets fails"); return(-1);}
	if(phd->hd_scmdvirt[0] == 0)strcpy(phd->hd_scmdvirt,phd->hd_scmdact);
	return(0);
}
/* 
   check() -- verify login name and password
   phd    = login,passwd
   fverify  = 1 if password must check
   Returns 1 if password is ok, 0 if not.
*/
check(phd,fverify)	/* 1 if OK, 0 if not */
register struct header *phd;
int fverify;
{
	char *sencpasswd, *u, *nullstr = "";
	struct passwd *pwd;
#ifdef CRN
	struct gecos *gcos;
#endif CRN
	if(phd->hd_snto[0] == 0)return(!fverify);
	debug("check: phd->hd_snto = %s\n", phd->hd_snto );
	if(!goodacctname(phd->hd_snto))return(!fverify);
	pwd = getpwnam(phd->hd_snto);
	debug("got pwd=%d, pwd->pw_passwd = %s\n",pwd, pwd->pw_passwd);
	if(pwd == NULL)return(!fverify);
	if(*phd->hd_spasswd)sencpasswd = crypt(phd->hd_spasswd,pwd->pw_passwd);
	else sencpasswd = nullstr;
	debug("check: passwd(rcvd)=%s, passwd(file) = %s, passwd(encrypt)=%s\n", phd->hd_spasswd, pwd->pw_passwd, sencpasswd );

	status.muid = guid(pwd->pw_uid,pwd->pw_gid);
	status.mgid = pwd->pw_gid;
#ifdef CRN
	if( (gcos=pwgecos( pwd->pw_gecos )) == NULL )
		strcpy( status.jobno, MAGICCRN );
	else 
		strcpy( status.jobno, gcos->gc_crn );
#else CRN
	strcpy( status.jobno, "XYZZ");
#endif CRN
	strcpy(status.dir,pwd->pw_dir);
	strcpy(status.loginshell,pwd->pw_shell);
	u = status.loginshell;
	if(u[0] == 0 || strcmp("/bin/sbash",u) == 0)strcpy(u,Bsh);

	getpwdf(pwd);
	/* ignore network passwd */
	/* acct is not a pair, acct is not "network", passwd is incorrect,
	and verification is requested => passwd not ok */
	if(!facctpaircheck(phd) && strcmp(phd->hd_snto,"network") != 0
	&& strcmp(pwd->pw_passwd,sencpasswd) != 0 && fverify)
		return(0);
	return(1);	/* otherwise passwd ok */
	}
mread(b,n)
  register int n; {
	if(length <= 0)return(0);
	if(length < n)n = length;
	n = nread(b,n);
	if(n != BROKENREAD)length -= n;
	return(n);
	}
char mgetc(){			/* returns 0 if fail */
	register char c;
	register int n;
	char buf[3];
	if((n=nread(buf,3)) == BROKENREAD)return(0);
	if(n != 3){error("bad read %d",n); return(0); }
	c = buf[0];
	if(buf[1] != ' ' && buf[1] != ':'){error("Bad char %c",buf[1]); return(0); }
	length -= 3;
	if(length < 0){error("length wrong2 %ld",length); return(0); }
	return(c);
	}
/* read in string over the network wire */
/* put string in s, max length is maxlen */
mgets(s,maxlen)			/* returns 0 if ok, 1 if not */
  int maxlen;
  register char *s; {
	register char *q;
	register int n;
	char c;
	q = s;
	for(;;) {
		if((n=nread(&c,1)) == BROKENREAD){
			*s = 0;
			error("mgets %s",s);
			return(1);
			}
		if(n == 0)break;
		if(c == '\\'){
			if((n=nread(&c,1)) == BROKENREAD){
				*s = 0;
				error("mgets %s",s);
				return(1);
				}
			if(n == 0)break;
			}
		if(c == ' ')break;
		if(maxlen-- > 0) *s++ = c;
		}
	*s = 0;
	if(nread(&c,1) == BROKENREAD){
		error("mgets %s",s);
		return(1);
		}
	length -= (s - q + 2);
	if(length < 0){error("length wrong1 %ld %s",length,q); return(-1); }
	if(maxlen < 0)
		error("mgets - string too long");
	return(0);
	}
mgetcmd(s)			/* returns 0 if succeed, 1 otherwise */
  char *s; {
	int i,n;
	char c;
	i = 0;
	for(;;){
		if((n=nread(&c,1)) == BROKENREAD){
			s[i] = 0;
			error("mgetcmd %s",s);
			return(1);
			}
		if(n <= 0 || c == '\n')break;
		if(c == '\\'){
			if(nread(&c,1) == BROKENREAD){
				s[i] = 0;
				error("mgetcmd %s",s);
				return(1);
				}
			length--;
			}
		s[i++] = c;
		length--;
		}
	s[i] = 0;
	length--;
	return(0);
	}
increment(s)
 char *s; {
	int i;
	char *p;
	i = strlen(s) - 1;
	while(s[i] == '9')i--;
	if(s[i] < '0' || s[i] > '9'){
		p = s+i+1;
		while(*p)*p++ = '0';
		return;
		}
	(s[i])++;
	i++;
	while(s[i])s[i++] = '0';
	return;
	}
/* gather 24-hour stats and  mail to STATADDR */
/* should also gather stats on # error msgs */
dumpit(currt)
  long currt; {
	register struct dumpstruc *p = &dump;
	register int ntot;
	long elapt;
	double cputime,utime,stime,bs,rawbs;
	char *sstartt;
	FILE *fdm;
	char froma[30];
	struct tms tbf;

	/* if STATADDR is a file, the mail program this call will
	   ultimately execute must be able to deal with it,
	   and the remote mail program must be able to write on the
	   file, i.e. mode 666 */
	sprintf(froma,"%s=>",longname(local));
	strcat(froma,longname(remote));
	fdm = mailopen(STATADDR,froma,1,0);
	if(fdm == NULL)return;

	/* calculate times */
	elapt = currt - dump.longtime;
	ntot = p->nnetcp + p->nnetmail + p->nsmail + p->nnetlpr
		+ p->nresp + p->nnet;
	sstartt = ctime(&dump.longtime) + 4;
	sstartt[strlen(sstartt) - 9] = 0;

	times(&tbf);
# ifndef NOFP
	utime = tbf.tms_utime + tbf.tms_cutime;
	stime = tbf.tms_stime + tbf.tms_cstime;
	cputime = utime + stime;
	if(elapt > 0)cputime = (cputime/elapt) * 100.0;
	else cputime = 0.0;
	utime = utime/60.0;
	stime = stime/60.0;
	cputime = cputime/60.0;
	bs = p->bytetot;
	if(p->elaptot > 0)bs = bs /p->elaptot;
	else bs = 0.0;
# endif NOFP

	/* print out the statistics */
	fprintf(fdm,"Subject: %s, %s, time %s\n",
		froma,sstartt, comptime(elapt));
	fprintf(fdm,"Command summary:\n");
	fprintf(fdm,"\t# sent %d\t# pass_thru %d\t# rcv %d:\t# netcp %d\n",
		p->nsend,p->npass,ntot,p->nnetcp);
	fprintf(fdm,"\t# netlpr %d\t# netmail %d\t# sendbmail %d\t# resp %d\n",
		p->nnetlpr,p->nnetmail,p->nsmail,p->nresp);
	fprintf(fdm,"Protocol summary:\n");
	fprintf(fdm,"\t# pk_sent %d\t# pk_rcv %d\t# b_sent %ld\t# b_rcv %ld\n",
		p->npacksent,p->npackrcv,p->nbytesent, p->nbytercv);
	fprintf(fdm,
		"\t# send_fails %d\t# retrans %d\t# abn %d\t\t# cksum_errs %d\n",
		p->nsendfail,p->nretrans, p->nabnormal,p->ncksum);
# ifndef NOFP
	fprintf(fdm,"Load:\tuser %4.1f\tsys %4.1f\tpct %5.2f\trate %6.1f\n",
		utime,stime,cputime,bs);
	rawbs = p->brawtot*100L;
	rawbs = rawbs / linechars();
	fprintf(fdm,"\trawbytes %ld\tuse %4.1f\n", p->brawtot,rawbs);
# endif NOFP
	mailclose(fdm);

	/* reset counters */
	p->nbytesent = p->nbytercv = p->elaptot = p->bytetot = 0L;
	p->nretrans = p->nloop = p->nabnormal = p->ncksum = 0;
	p->npacksent = p->npackrcv = p->nnetcp = p->nnetmail = 0;
	p->nsmail = p->nnetlpr = p->nnet = p->npass = 0;
	p->nsend = p->nsendfail = 0;
	dump.longtime = currt;
	}
/* returns 1 if n is ok, 0 if not */
goodacctname(n)
  char *n; {
	int i;
	i = -1;
	while(btable[++i].bname)
		if(strcmp(btable[i].bname,n) == 0 &&
			local == btable[i].bmach)return(0);
	return(1);
	}
demask(s)
  register char *s; {
/*
	static char buf[20];
	char skey[30];
	makeuukey(skey,status.login,local);
	strcpy(s,nbsdecrypt(s,skey,buf));
*/
	while(*s){
		*s &= 0177;		/* strip quote bites */
		*s++ ^= 040;		/* invert upper-lower */
		}
	}
/*VARARGS0*/
mreopen(fsendtofmach,phd,sfn,a,b,c){
/* simply handles errors by giving error msg */
	if(freopen(a,b,c) == NULL)
		errormsg(fsendtofmach,phd,sfn,"%s: %s",a,sys_errlist[errno]);
}
/* 
	addtopub(string, args)

	add a message to the public logfile /usr/net/logfile.
	note that the file must be writeable by everyone
	if error messages from the netrcv subroutine
	such as chdir errors are to be noticed.
*/
/*VARARGS0*/
addtopublic(s,a,b,c,d,e,f,g,h,i,j,k,l,m,n)
char *s;
{
	static FILE *log = NULL;
	if(log == NULL){
		if(stat(publogfile,&statbuf) < 0)return;
		log = fopen(publogfile,"a");
		if(log == NULL)return;
		}
	fseek(log,0L,2);
	fprintf(log,s,a,b,c,d,e,f,g,h,i,j,k,l,m,n);
	fflush(log);
	}
/* set up a dummy environment for v7 /bin/sh */
setenv(home)
  char *home; {
	static char *env[3],benv[2][50];
	env[0] = benv[0];
	env[1] = benv[1];
#ifdef CCV7
	strcpy( env[0], "PATH=:.:/usr/cc/bin:/usr/ucb/bin" );
#else CCV7
	strcpy(env[0],"PATH=:/bin:/usr/bin");
#endif CCV7
	sprintf(env[1],"HOME=%s",home);
	env[2] = 0;
	environ = env;
	}
/* 
	errormsg(fsendtofmach,phd,sfn,"string",arg(s))
	
	Sends error message to user.
	If fsendtofmach=TRUE, send to phd->hd_mchfrom, otherwise
	send to phd->hd_mchto.
	Also, if error occured during return of a "response",
	send to local machine.

	Note that errormsg can be called by the netrcv subroutine
	after the setuid() call to the specific user, so the 
	user must be able to get off an error msg back to him,
	and to write in the two log files.
	Can't use -w,-x,-y,-z for the net cmd because must be root for those.

	If sfn != NULL, then unlink sfn before exiting.
*/
/*VARARGS0*/
errormsg(fsendtofmach,phd,sfn,s,a,b,c,d,e,f,g,h)
char fsendtofmach;				
struct header *phd;
char *sfn,*s;
{
	int rcode;
	char errstr[BUFSIZ], cmdstr[BUFSIZ], rcmd[BUFSIZ];
	char toadd[FNS], fromadd[FNS], mchto, mchfrom;
	char snto[FNS], snfrom[FNS];

	if(phd->hd_sttyname[0] == 0)strcpy(phd->hd_sttyname,"/dev/ttyx");
	/* will send to toadd, from fromadd */
	if(!fsendtofmach || strcmp(phd->hd_scmdvirt,"response") == 0){
		/* send to tomach mach, thus send to toaddr. */
		/* if this is an error during a response, send to local mach. */
		strcpy(toadd,  phd->hd_addrto);
		strcpy(fromadd,phd->hd_addrfrom);
	}
	else {		/* send to remote mach, thus send back to addrfrom*/
		strcpy(toadd,  phd->hd_addrfrom);
		strcpy(fromadd,phd->hd_addrto);
	}
	sprintf(errstr,"Error: ");
	sprintf(cmdstr,s,a,b,c,d,e,f,g,h);
	strcat(errstr,cmdstr);
	strcat(errstr,"\n");
	addtolog(remote,errstr);
	addtopublic(errstr);

	mchto =   MchSFromAddr(snto,toadd); 
	mchfrom = MchSFromAddr(snfrom,fromadd);

	sprintf(rcmd,
"%s %s %s %lo %c %s \"'%s'\" %ld -t %s -f %s -x %ld -y %s -c \"'%s'\" -e %ld",
	MWRITECMD, snto, phd->hd_sttyname, phd->hd_lttytime, 
	local, snfrom,phd->hd_scmdvirt, phd->hd_ltimesent-TIMEBASE,
	toadd, fromadd, phd->hd_lttytime, phd->hd_sttyname, phd->hd_scmdvirt,
	phd->hd_ltimesent-TIMEBASE);

	if(mchto == local)
		sprintf(cmdstr, "echo \"%s\" | %s", errstr,rcmd);
	else 
		sprintf(cmdstr,
		"echo \"%s\" | %s -m%c -b -c errormessage -l network - %s",
			errstr,netcmd,mchto,rcmd);
	rcode = system(cmdstr);
	debug( "errormsg: cmdstr = %s\n", cmdstr );
	debug( "errormsg: rcode = %d\n", rcode );
	if(sfn != NULL)unlink(sfn);
	exit(EX_USAGE);
	}
handlekill(){	/* SIGTERM signal */
	long t;
	/*
	t = gettime();
	dumpit(t);
	*/
# ifdef NETLDISC
	/* turn off net line discipline if possible */
	netd.dp_linedis = 0;
	ioctl(netd.dp_linefd,TIOCSETD,&netd.dp_linedis);
	close(netd.dp_linefd);
	printf("Network line discipline turned off.\n");
# endif NETLDISC
	exit(EX_OK);	/* kill myself */
	}

/* check a request to see if it is an acct pair */
/* returns 1 if it is, 0 if not */
static facctpaircheck(phd)
register struct header *phd;
{
	return(0);
}