BBN-Vax-TCP/src/telnet/netser.c
#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