BBN-Vax-TCP/src/telnet/netser.c

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

#include "signal.h"
#include "stdio.h"
#include "telnet.h"
#include "netlib.h"
#ifdef TCP
#include "con.h"
#define SIG_NETINT SIGURG
#include "errno.h"
#endif TCP
#ifdef NCP
#include "ncpopen.h"
#endif NCP
#ifndef MODTTY
#include "sgtty.h"
#endif MODTTY
#ifdef MODTTY
#include "modtty.h"
#endif MODTTY

/*
 * New Telnet Network Server
 * Modified Jan 4 1978 by BBN(dan) to use explicit byte count
 * instead of null when copying from pty to network.
 * This fixes the bug whereby nulls in output would chop out
 * pieces of it...
 *
 * General reformatting and deletion of Old Telnet code Jan 20 '79 BBN(dan)
 * No code was actually changed (as verified by a cmp of the object files).
 *
 * Use /dev/net/longhost and expect long host numbers from fstat
 * jsq BBN 3-14-79
 * Use /dev/net/ncp jsq BBN 4-27-79
 * Try to accept binary mode jsq BBN 2Aug79
 *
 * Arrange for each side to kill the other if it dies.	jsq BBN 18Aug79
 *
 * ?Nov79 jsq BBN remember to close files if fork for netgrf in main fails.
 * 13Feb80 dan BBN if MODTTY, do modtty to get real intr char.
 * 22Oct80 dan BBN change to use signal.h; change
 *	    fdprintf(fout...) to printf(...) since fout == 1 anyway;
 *	    change to use sgtty.h.
 * 20Feb81 ado BBN tcp version, for vax in-kernel tcp.
 * 7mar81 bpc BBN changed to send interrupt character properly, even in the 
 *                absence of modtty, but with V7
 * 11Sep81 rfg BBN changed telcmd to do proper echo option negotiation for 
 *		  the VAX
 * 12nov81 ers BBN changed code in interrupt() to be ifdef'd on TIOCGETC
 *		instead of on V7.
 */

#define LEVEL8 RAW

/* Parameters */

#define NETBUFSIZE 60	/* Number of characters in network buffer */
char firstpty = 'A';	/* Letter of first pseudo-teletype */

/* -------------------------- G L O B A L S -------------------------------- */

int pid;		/* Process ID of subsidiary fork */
int netchan;		/* Channel descriptor for network */
				/* channel used for TELNET data */
int ptyin;		/* Channel number of PTY used for input */
int ptyout;		/* Channel number of PTY used for output */
int logecho = 1;	/* ignore 'do echo' on login */
char echoflag;		/* Indicates 'will echo' has been sent */
int BinaryIn;		/* binary option on */
int BinaryOut;		/* binary option on */
char *Now();		/* returns string with current date */
char *progname; 	/* who are we */
int junk;		/* Somewhere to throw away things */
#ifdef NCP
#define NETPARAMS ncpopen
#endif
#ifdef TCP
#define NETPARAMS con
#endif TCP
struct NETPARAMS openparams;
int pty;		/* Pty name */
int debugsw = 0, debugsok = SERVER_SOCKET;
char *arg1p;		/* Pointer to arg[1] */
int inter;		/* Indication that an interrupt came in */
int seq();
int ins();		/* INS interrupt routine */



/* -------------------------- M A I N ------------------------------- */
/*
 * Loop. Each time a connection becomes established, fork and call netgrf.
 * Wait for child (netgrf) to exit (which should be immediately).
 */

main(argcnt,argvec)
    int argcnt;
    char *argvec[];
{
    extern char * atoiv();
    extern char *errmsg();

    if ((argcnt >= 2) && (debugsw=seq(argvec[1],"-debug"))
	&& (argcnt >= 3) && (*atoiv(argvec[2],&debugsok)))
	    printf("Bad socket, sorry.\n"), exit(-1);

/*  if(fork()) exit(0);     /* so we get adopted by init immediately */

    progname = argvec[0];
    setbuf(stdout, NULL);
	    signal(SIGHUP,  SIG_IGN);
    signal(SIGINT,  SIG_IGN);
    signal(SIGQUIT, SIG_IGN);
    signal(SIGTERM, SIG_IGN);
    signal(SIG_NETINT,  SIG_IGN);
    arg1p = argvec[1];
    close(2); dup(1);		  /* redirect error dev */
    printf("Telnet Server awaiting connection %s", Now());
    for (;;)
	{
#ifdef NCP
	openparams.o_type = SERVER;	 /* Server ICP */
	openparams.o_nomall = 1024; /* For block-mode terminals */
	openparams.o_fskt = 0;
	openparams.o_lskt = SERVER_SOCKET;
	openparams.o_host = 0;
	while ((netchan = open("/dev/net/ncp",&openparams)) < 0)
	    {
		printf("Can't open /dev/net/ncp: %s; %s", errmsg(0), Now());
		sleep(DAEMSLEEP);
	    }
#endif NCP
#ifdef TCP
	if(fork()) exit(0);	/* so tcb's show right owner */
	openparams.c_mode = CONTCP | ((debugsw)?CONDEBUG:0);
	openparams.c_sbufs = 2;
	openparams.c_rbufs = 1;
			/* server socket is also server port */
	openparams.c_lport = (debugsw?debugsok:SERVER_SOCKET);
	openparams.c_fport =
	  openparams.c_lo = openparams.c_hi = 0;
	mknetaddr(openparams.c_fcon, 0, 0, 0);
	mkanyhost(openparams.c_lcon);
	while ((netchan = open("/dev/net/tcp",&openparams)) < 0)
	    {
	        printf("can't open /dev/net/tcp: %s; %s",
		 errmsg(0), Now());
		sleep(DAEMSLEEP);
	     }
	ioctl(netchan, NETSETE, NULL);
#endif TCP
	if ((pid = fork())<0)
	    {
	    printf("No processes left! %s", Now());
	    }
	else if (pid==0)	  /* Child invokes netgrf */
	    {
	    netgrf();
	    exit(0);
	    }
	else			  /* Parent waits for child */
	    {
	    while(pid != wait(&junk));
	    }
	close(netchan);
	}
    }

/* -------------------------- N E T G R F --------------------------- */
/*
 * Fork. Parent exits immediately; child gets a pty, sets command line,
 * and forks again. Child is telxmt, parent is telrcv.
 */
netgrf()
{

    if ((pid=fork())>0)
	exit(0);		  /* Parent exits immediately so logger wait wakes up */
    else if (pid<0)
	{
	printf("No processes left %s", Now());
	write(netchan,"Sorry, not enough resources available.\r\n",41);
	exit(1);
	}
    if (openpty()<0)
	{
	printf("No ptys %s", Now());
	write(netchan,"Sorry, no ptys\r\n",16);
	exit(1);
	}
    setss();

    if ((pid = fork())<0)
	{
	printf("No processes %s", Now());
	write(netchan,"Sorry, no processes\r\n",21);
	exit(1);
	}
    if (pid==0)
	telxmt();	   /* Child handles net xmit side */
    else
	telrcv();	   /* Parent handles net receive side */
}


/* -------------------------- T E L R C V --------------------------------- */
/*
 * Read from net via telgetc; call telcmd on IAC-preceeded stuff,
 * send everything else to pty. On CRLF/CRNUL sequences, only the CR
 * is passed to pty.
 */
telrcv()
{
    unsigned char c;
    extern int BinaryOut;

    sleep(2);			  /* Give getty a chance to do stty */

    for(;;)
    {
	c = telgetc();		      /* Fetch a character */
	if (c==IAC)		      /* IAC? */
	    telcmd();			/* Yes, handle special command */
	else if (inter <= 0)
	{
	    write(ptyin, &c, 1);	  /* Otherwise, just enter it */
	    if (!BinaryOut && (c=='\r'))	      /* CR? */
	    {
		c = telgetc();		/* Yes, get next */
		if (c && c!='\n')	/* If not LF or NUL, write it anyway */
		    write(ptyin, &c, 1);
	    }
	}
    }
}

/* -------------------------- T E L C M D --------------------------- */
/*
 * Process New Telnet protocol. Reply to option negotiations.
 */
telcmd()
{
    unsigned char negbuf[3];
    unsigned char command;
    struct sgttyb status;	  /* Buffer for stty and gtty */
    static int amode;
    extern int BinaryIn, BinaryOut;
    command = telgetc();	  /* Get a character */
    switch (command)
	{
	case BREAK:
	case IP:
	    interrupt(ptyin);
	    break;
	case AYT:
	    write(netchan, "\007", 1);
	    break;
	case EC:
	case EL:
	    gtty(ptyin, &status);
	    write(ptyin, (command == EC) ? &status.sg_erase : &status.sg_kill, 1);
	    break;
#    ifdef TCP
	case DM:
	    inter = ChkUrgent();
	    break;
#    endif TCP

	case SB:		    /* Begin subnegotiation? */
/* Look for end of subnegotiation */
	    do
		{
		while(command != IAC)
		    command = telgetc();
		}
	    while (telgetc() != SE);
	    break;
	case WILL:
	case WONT:
	case DO:
	case DONT:

/* Negotiate. Only echo option implemented. */
/* and binary option.  jsq BBN 8/2/79 */

	    negbuf[0] = IAC;	      /* Interpete As Command */
	    negbuf[1] = DONT+WONT-((command+1)&0376); /* Negative ack. */
	    negbuf[2] = telgetc();    /* Set code for option */
	    if (negbuf[2] == 1 && (command == DO || command == DONT))  /* Echo option? */
		{
		gtty(ptyin,&status);
		if (command == DO)    /* User wants us to echo */
		    {
		    if (!logecho)	/* ignore login 'will echo' (getty does it for us) */
			status.sg_flags |= ECHO;
		    negbuf[1] = WILL;
		    }
		else		      /* User doesn't want us to echo */
		    {
			status.sg_flags &= ~ECHO;
			negbuf[1] = WONT;
		    }
		logecho = 0;
		stty(ptyin, &status);  /* Set desired echo mode */
		if (echoflag == 0)    /* Was this a response to our WILL ECHO? */
		    {
		    echoflag++; 	/* If so don't do anything */
			break;
		    }
		}

#define LEVMASK (~ECHO) /* everything but echo */

		if (negbuf[2] == 0) {	/* binary negotiation */
			gtty(ptyin,&status);
			if (command == DO) {
			    if ((BinaryIn + BinaryOut) == 0) {
				amode = status.sg_flags&LEVMASK;
				status.sg_flags = ((status.sg_flags & ~LEVMASK) | (LEVEL8|RAW));
			    }
			    BinaryIn = 1;
			    negbuf[1] = WILL;
			} else if (command == WILL) {
			    if ((BinaryIn + BinaryOut) == 0) {
				amode = status.sg_flags&LEVMASK;
				status.sg_flags = ((status.sg_flags & ~LEVMASK) | (LEVEL8|RAW));
			    }
			    BinaryOut = 1;
			    negbuf[1] = DO;
			} else if (command == DONT) {
			    if (BinaryIn && ((BinaryIn + BinaryOut) == 1))
				status.sg_flags = ((status.sg_flags & ~LEVMASK) | (amode & ~LEVEL8));
			    BinaryIn = 0;
			    negbuf[1] = WONT;
			} else {
			    if (BinaryOut && ((BinaryIn + BinaryOut) == 1))
				status.sg_flags = ((status.sg_flags & ~LEVMASK) | (amode & ~LEVEL8));
			    BinaryOut = 0;
			    negbuf[1] = DONT;
			}
			stty(ptyin, &status);
		}
	    write(netchan,negbuf,3);  /* Send off confirmation/denial to net */
	    break;
	case IAC:			    /* Not really a command */
	    write(ptyin, &command, 1);	      /* but a character */
	    break;
	}
    }

/* -------------------------- T E L X M T --------------------------- */
/*
 * Copy from pty to net. Double IACs.
 */
char ttybuf[NETBUFSIZE+1];	/* This size is best for NetUNIX */

telxmt()
{
    int bytcnt;
    register char *s1,*s2;

/* Send 'WILL ECHO' and a reminder */

    write(netchan,"\377\373\001\r\n",5);
    if (netchan > 2)
	printf("%s %s %s", progname, arg1p, Now());

/* Copy characters from output buffer to network */

    while((bytcnt = read(ptyout, ttybuf, sizeof(ttybuf))) >= 0)
	{
	s1 = ttybuf;
	s2 = s1;     /* Initialize pointers */
	for (;;)
	    {
	    while ((s1 < &ttybuf[bytcnt]) && (((*s1) & 0377) != IAC))
		s1++;	 /* Find end or IAC */
	    if (s1 < &ttybuf[bytcnt])
		{
		write(netchan, s2, s1 - s2 + 1);
		s2 = s1;
		s1++;
		}
	    else
		{
		write(netchan, s2, s1 - s2);
		break;
		}
	    }
	}
}

/* -------------------------- O P E N P T Y ------------------------- */

char *ptyname = "/dev/ptyx";	/* Where to look for pseudo-teletypes */

extern int errno;		/* Error code on failure */

int openpty()
{
    register char *s1;

/* Find end of device name */

    s1 = ptyname;
    while (*s1++)
	;
    s1 -= 2;

    *s1 = firstpty; /* Make name of first PTY */
    for (;;)	    /* Step through ptys looking for an unused one */
	{
	ptyin = open(ptyname, 2);  /* Try this PTY */
	ptyout = ptyin; 	   /* Holdover from pipe version */
	if (ptyin>=0)
	    {
	    pty = (*s1)&0177;
	    return(ptyin);
	    }
	if (errno==2)
	    return(-1);   /* No such file.  Assume end of PTYs */
	(*s1)++;
	}
}

/* -------------------------- S E T S S ----------------------------- */

char argstr[] = "ttyX ";

setss()
{
    register char *p, *q;
    struct NETPARAMS statparams;

    if ((q=arg1p) == 0)
	return;
    argstr[3] = pty;
    p = argstr;
    while (*p)
	{
	if (*q)
	    *q++ = *p++;
	else
	    return;
	}
#ifdef NCP
    if (fstat(netchan,&statparams) >= 0)
#endif NCP
#ifdef TCP
    if (ioctl(netchan,NETGETS,&statparams) >= 0)
#endif TCP
	{
#	ifdef NCP
	    p = hostname(statparams.o_host);
#	endif NCP
#	ifdef TCP
	    p = hostname(statparams.c_fcon);
#	endif TCP
	while (*p)
	    {
	    if (*q)
		*q++ = *p++;
	    else
		return;
	    }
	}
	*q = '\0';
}

/* -------------------------- T E L G E T C ------------------------- */

telgetc()
{
    unsigned char c;
    register int i;

#   ifdef NCP
    struct sgttyb ttytype;
    i = read(netchan,&c,1);
    if (inter)			/* check for interrupts */
    {
	inter = 0;		/* DM will be ignored */
	gtty(ptyout, &ttytype); /* return NUL if in raw mode, else DEL */
	return((ttytype.sg_flags & RAW) ? 0 : 0177);
    }
#   endif NCP
#   ifdef TCP
    while ((i=read(netchan,&c,1)) < 0)
    {
	extern errno;
	struct netstate stat;

	if (errno == ENETSTAT)   
	    if (ioctl (netchan, NETGETS, &stat) < 0 || 
	        (stat.n_state != URXTIMO && stat.n_state != UURGENT))
		{
		    i = 0;
		    break;
		}
    }
#   endif TCP

    if (i == 0)
    {
	kill(pid, 9);		/* Kill other side */
	exit(1);
    }

    return(c&0377);
}

/*--------------------------- I N S --------------------------------- */

ins()
{
    inter++;		    /* note that an interrupt has occurred */
    signal(SIG_NETINT, ins);
}

/* -------------------------- N O W --------------------------------- */

char *Now()
{
		long now;
		extern char *ctime();

		time(&now);
		return(ctime(&now));
}

/* -------------------------- I N T E R R U P T --------------------- */
/*
 * Send interrupt to process on other side of pty.
 * If it is in raw mode, just write NUL; otherwise, write intr char.
 * Intr char is DEL unless modtty implemented, in which case it is
 * whatever the user said it is.
 *  .. 5mar81 bpc .. Send proper interupt char even if not moddty (but
 *        V7)
 * 12nov81 ers Changed to ifdef on TIOCGETC, instead of on V7.
 */
interrupt(fd)
    int fd;
{
    struct sgttyb status;
#ifndef MODTTY
#   ifdef TIOCGETC
	struct tchars tchars;
#   endif
#endif
    char cint;
#ifdef MODTTY
    struct modes modbuf;
    int old12;
#endif

    gtty(fd, &status);
    if (status.sg_flags & RAW)
        write(fd, "\0", 1);
    else
    {
        cint = '\177';
#ifdef MODTTY
        old12 = signal(12, 1);  /* Don't want "bad system call" signal */
        if (modtty(fd, M_GET|M_MODES, &modbuf, sizeof(modbuf)) != -1)
            cint = modbuf.t_intr;
        signal(12, old12);
#else
#ifdef TIOCGETC
	if (ioctl (fd, TIOCGETC, &tchars) != -1)
	    cint = tchars.t_intrc;
#endif TIOCGETC
#endif MODTTY
        write(fd, &cint, 1);
    }
}
/*
 * ----------------------- C h k U r g e n t ------------------------
 *
 * called to check if connection is still in urgent state.
 *
 */

#ifdef TCP
ChkUrgent()
{
	struct netstate tstat;
	ioctl(netchan,NETGETS, &tstat);
	return(tstat.n_state & UURGENT);
 } 
#endif TCP