xref: /csrg-svn/libexec/telnetd/telnetd.c (revision 32097)
121182Sdist /*
227898Skarels  * Copyright (c) 1983,1986 Regents of the University of California.
321182Sdist  * All rights reserved.  The Berkeley software License Agreement
421182Sdist  * specifies the terms and conditions for redistribution.
521182Sdist  */
621182Sdist 
76295Sroot #ifndef lint
821182Sdist char copyright[] =
921182Sdist "@(#) Copyright (c) 1983 Regents of the University of California.\n\
1021182Sdist  All rights reserved.\n";
1121182Sdist #endif not lint
126295Sroot 
1321182Sdist #ifndef lint
14*32097Sminshall static char sccsid[] = "@(#)telnetd.c	5.20 (Berkeley) 09/02/87";
1521182Sdist #endif not lint
1621182Sdist 
176002Sroot /*
1827898Skarels  * Telnet server.
196002Sroot  */
2027898Skarels #include <sys/param.h>
219218Ssam #include <sys/socket.h>
2213608Ssam #include <sys/wait.h>
2317583Ssam #include <sys/file.h>
2420188Skarels #include <sys/stat.h>
2527185Sminshall #include <sys/time.h>
269218Ssam 
279218Ssam #include <netinet/in.h>
289218Ssam 
2912216Ssam #include <arpa/telnet.h>
3012216Ssam 
316002Sroot #include <stdio.h>
326002Sroot #include <signal.h>
336002Sroot #include <errno.h>
346002Sroot #include <sgtty.h>
358346Ssam #include <netdb.h>
3617187Sralph #include <syslog.h>
3727649Sminshall #include <ctype.h>
389218Ssam 
3927983Sminshall #define	OPT_NO			0		/* won't do this option */
4027983Sminshall #define	OPT_YES			1		/* will do this option */
4127983Sminshall #define	OPT_YES_BUT_ALWAYS_LOOK	2
4227983Sminshall #define	OPT_NO_BUT_ALWAYS_LOOK	3
436002Sroot char	hisopts[256];
446002Sroot char	myopts[256];
456002Sroot 
466002Sroot char	doopt[] = { IAC, DO, '%', 'c', 0 };
476002Sroot char	dont[] = { IAC, DONT, '%', 'c', 0 };
486002Sroot char	will[] = { IAC, WILL, '%', 'c', 0 };
496002Sroot char	wont[] = { IAC, WONT, '%', 'c', 0 };
506002Sroot 
516002Sroot /*
526002Sroot  * I/O data buffers, pointers, and counters.
536002Sroot  */
546002Sroot char	ptyibuf[BUFSIZ], *ptyip = ptyibuf;
5527649Sminshall 
566002Sroot char	ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf;
5727649Sminshall 
586002Sroot char	netibuf[BUFSIZ], *netip = netibuf;
5927649Sminshall #define	NIACCUM(c)	{   *netip++ = c; \
6027649Sminshall 			    ncc++; \
6127649Sminshall 			}
6227649Sminshall 
636388Ssam char	netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf;
6427185Sminshall char	*neturg = 0;		/* one past last bye of urgent data */
6527649Sminshall 	/* the remote system seems to NOT be an old 4.2 */
6627649Sminshall int	not42 = 1;
6727649Sminshall 
6827649Sminshall 
6927797Sminshall char BANNER1[] = "\r\n\r\n4.3 BSD UNIX (",
7027797Sminshall     BANNER2[] = ")\r\n\r\0\r\n\r\0";
7127797Sminshall 
7227983Sminshall 		/* buffer for sub-options */
7327983Sminshall char	subbuffer[100], *subpointer= subbuffer, *subend= subbuffer;
7427649Sminshall #define	SB_CLEAR()	subpointer = subbuffer;
7527983Sminshall #define	SB_TERM()	{ subend = subpointer; SB_CLEAR(); }
7627649Sminshall #define	SB_ACCUM(c)	if (subpointer < (subbuffer+sizeof subbuffer)) { \
7727649Sminshall 				*subpointer++ = (c); \
7827649Sminshall 			}
7927983Sminshall #define	SB_GET()	((*subpointer++)&0xff)
8027983Sminshall #define	SB_EOF()	(subpointer >= subend)
8127649Sminshall 
826002Sroot int	pcc, ncc;
836002Sroot 
846002Sroot int	pty, net;
856002Sroot int	inter;
8613799Ssam extern	char **environ;
876002Sroot extern	int errno;
8820188Skarels char	*line;
8927185Sminshall int	SYNCHing = 0;		/* we are in TELNET SYNCH mode */
9027185Sminshall /*
9127185Sminshall  * The following are some clocks used to decide how to interpret
9227185Sminshall  * the relationship between various variables.
9327185Sminshall  */
946002Sroot 
9527185Sminshall struct {
9627185Sminshall     int
9727185Sminshall 	system,			/* what the current time is */
9827185Sminshall 	echotoggle,		/* last time user entered echo character */
9927185Sminshall 	modenegotiated,		/* last time operating mode negotiated */
10027185Sminshall 	didnetreceive,		/* last time we read data from network */
10127983Sminshall 	ttypeopt,		/* ttype will/won't received */
10227983Sminshall 	ttypesubopt,		/* ttype subopt is received */
10327983Sminshall 	getterminal,		/* time started to get terminal information */
10427185Sminshall 	gotDM;			/* when did we last see a data mark */
10527185Sminshall } clocks;
10627185Sminshall 
10727983Sminshall #define	settimer(x)	(clocks.x = ++clocks.system)
10827983Sminshall #define	sequenceIs(x,y)	(clocks.x < clocks.y)
10927185Sminshall 
1106002Sroot main(argc, argv)
1116002Sroot 	char *argv[];
1126002Sroot {
11316371Skarels 	struct sockaddr_in from;
11417156Ssam 	int on = 1, fromlen;
1156002Sroot 
11627185Sminshall #if	defined(DEBUG)
11727185Sminshall 	{
11827185Sminshall 	    int s, ns, foo;
11927185Sminshall 	    struct servent *sp;
12027185Sminshall 	    static struct sockaddr_in sin = { AF_INET };
12127185Sminshall 
12227185Sminshall 	    sp = getservbyname("telnet", "tcp");
12327185Sminshall 	    if (sp == 0) {
12427185Sminshall 		    fprintf(stderr, "telnetd: tcp/telnet: unknown service\n");
12527185Sminshall 		    exit(1);
12627185Sminshall 	    }
12727185Sminshall 	    sin.sin_port = sp->s_port;
12827185Sminshall 	    argc--, argv++;
12927185Sminshall 	    if (argc > 0) {
13027185Sminshall 		    sin.sin_port = atoi(*argv);
13127185Sminshall 		    sin.sin_port = htons((u_short)sin.sin_port);
13227185Sminshall 	    }
13327185Sminshall 
13427185Sminshall 	    s = socket(AF_INET, SOCK_STREAM, 0);
13527185Sminshall 	    if (s < 0) {
13627185Sminshall 		    perror("telnetd: socket");;
13727185Sminshall 		    exit(1);
13827185Sminshall 	    }
13927185Sminshall 	    if (bind(s, &sin, sizeof sin) < 0) {
14027185Sminshall 		perror("bind");
14127185Sminshall 		exit(1);
14227185Sminshall 	    }
14327185Sminshall 	    if (listen(s, 1) < 0) {
14427185Sminshall 		perror("listen");
14527185Sminshall 		exit(1);
14627185Sminshall 	    }
14727185Sminshall 	    foo = sizeof sin;
14827185Sminshall 	    ns = accept(s, &sin, &foo);
14927185Sminshall 	    if (ns < 0) {
15027185Sminshall 		perror("accept");
15127185Sminshall 		exit(1);
15227185Sminshall 	    }
15327185Sminshall 	    dup2(ns, 0);
15427185Sminshall 	    close(s);
15527185Sminshall 	}
15627185Sminshall #endif	/* defined(DEBUG) */
15724855Seric 	openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON);
15816371Skarels 	fromlen = sizeof (from);
15916371Skarels 	if (getpeername(0, &from, &fromlen) < 0) {
16016371Skarels 		fprintf(stderr, "%s: ", argv[0]);
16116371Skarels 		perror("getpeername");
16216371Skarels 		_exit(1);
1638346Ssam 	}
16417156Ssam 	if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) {
16517187Sralph 		syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
16610418Ssam 	}
16716371Skarels 	doit(0, &from);
1686002Sroot }
1696002Sroot 
17027983Sminshall char	*terminaltype = 0;
17127983Sminshall char	*envinit[2];
17227983Sminshall int	cleanup();
17327649Sminshall 
17427649Sminshall /*
17527983Sminshall  * ttloop
17627649Sminshall  *
17727983Sminshall  *	A small subroutine to flush the network output buffer, get some data
17827983Sminshall  * from the network, and pass it through the telnet state machine.  We
17927983Sminshall  * also flush the pty input buffer (by dropping its data) if it becomes
18027983Sminshall  * too full.
18127983Sminshall  */
18227983Sminshall 
18327983Sminshall void
18427983Sminshall ttloop()
18527983Sminshall {
18627983Sminshall     if (nfrontp-nbackp) {
18727983Sminshall 	netflush();
18827983Sminshall     }
18927983Sminshall     ncc = read(net, netibuf, sizeof netibuf);
19027983Sminshall     if (ncc < 0) {
19127983Sminshall 	syslog(LOG_INFO, "ttloop:  read: %m\n");
19228044Sminshall 	exit(1);
19328044Sminshall     } else if (ncc == 0) {
19428044Sminshall 	syslog(LOG_INFO, "ttloop:  peer died: %m\n");
19528044Sminshall 	exit(1);
19627983Sminshall     }
19727983Sminshall     netip = netibuf;
19827983Sminshall     telrcv();			/* state machine */
19927983Sminshall     if (ncc > 0) {
20027983Sminshall 	pfrontp = pbackp = ptyobuf;
20127983Sminshall 	telrcv();
20227983Sminshall     }
20327983Sminshall }
20427983Sminshall 
20527983Sminshall /*
20627983Sminshall  * getterminaltype
20727649Sminshall  *
20827983Sminshall  *	Ask the other end to send along its terminal type.
20927983Sminshall  * Output is the variable terminaltype filled in.
21027649Sminshall  */
21127649Sminshall 
21227983Sminshall void
21327983Sminshall getterminaltype()
21427649Sminshall {
21527983Sminshall     static char sbuf[] = { IAC, DO, TELOPT_TTYPE };
21627649Sminshall 
21727983Sminshall     settimer(getterminal);
21827983Sminshall     bcopy(sbuf, nfrontp, sizeof sbuf);
21927983Sminshall     nfrontp += sizeof sbuf;
22028044Sminshall     hisopts[TELOPT_TTYPE] = OPT_YES_BUT_ALWAYS_LOOK;
22127983Sminshall     while (sequenceIs(ttypeopt, getterminal)) {
22227983Sminshall 	ttloop();
22327649Sminshall     }
22427983Sminshall     if (hisopts[TELOPT_TTYPE] == OPT_YES) {
22527983Sminshall 	static char sbbuf[] = { IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE };
22627983Sminshall 
22727983Sminshall 	bcopy(sbbuf, nfrontp, sizeof sbbuf);
22827983Sminshall 	nfrontp += sizeof sbbuf;
22927983Sminshall 	while (sequenceIs(ttypesubopt, getterminal)) {
23027983Sminshall 	    ttloop();
23127983Sminshall 	}
23227983Sminshall     }
23327649Sminshall }
23427649Sminshall 
2356002Sroot /*
2366002Sroot  * Get a pty, scan input lines.
2376002Sroot  */
23812683Ssam doit(f, who)
23912683Ssam 	int f;
24012683Ssam 	struct sockaddr_in *who;
2416002Sroot {
24220188Skarels 	char *host, *inet_ntoa();
24317583Ssam 	int i, p, t;
2446002Sroot 	struct sgttyb b;
24512683Ssam 	struct hostent *hp;
24627649Sminshall 	int c;
2476002Sroot 
24820188Skarels 	for (c = 'p'; c <= 's'; c++) {
24920188Skarels 		struct stat stb;
25020188Skarels 
25120188Skarels 		line = "/dev/ptyXX";
25220188Skarels 		line[strlen("/dev/pty")] = c;
25320188Skarels 		line[strlen("/dev/ptyp")] = '0';
25420188Skarels 		if (stat(line, &stb) < 0)
25520188Skarels 			break;
25617583Ssam 		for (i = 0; i < 16; i++) {
25720188Skarels 			line[strlen("/dev/ptyp")] = "0123456789abcdef"[i];
25820188Skarels 			p = open(line, 2);
25917583Ssam 			if (p > 0)
26017583Ssam 				goto gotpty;
26117583Ssam 		}
2626002Sroot 	}
2639244Ssam 	fatal(f, "All network ports in use");
2649244Ssam 	/*NOTREACHED*/
2656002Sroot gotpty:
2666002Sroot 	dup2(f, 0);
26720188Skarels 	line[strlen("/dev/")] = 't';
26817583Ssam 	t = open("/dev/tty", O_RDWR);
2696002Sroot 	if (t >= 0) {
2706002Sroot 		ioctl(t, TIOCNOTTY, 0);
2716002Sroot 		close(t);
2726002Sroot 	}
27320188Skarels 	t = open(line, O_RDWR);
2749244Ssam 	if (t < 0)
27520188Skarels 		fatalperror(f, line, errno);
2766002Sroot 	ioctl(t, TIOCGETP, &b);
2776388Ssam 	b.sg_flags = CRMOD|XTABS|ANYP;
2786002Sroot 	ioctl(t, TIOCSETP, &b);
2796388Ssam 	ioctl(p, TIOCGETP, &b);
2808379Ssam 	b.sg_flags &= ~ECHO;
2816388Ssam 	ioctl(p, TIOCSETP, &b);
28212683Ssam 	hp = gethostbyaddr(&who->sin_addr, sizeof (struct in_addr),
28312683Ssam 		who->sin_family);
28412683Ssam 	if (hp)
28512683Ssam 		host = hp->h_name;
28612683Ssam 	else
28717444Sralph 		host = inet_ntoa(who->sin_addr);
28827983Sminshall 
28927983Sminshall 	net = f;
29027983Sminshall 	pty = p;
29127983Sminshall 
29227983Sminshall 	/*
29327983Sminshall 	 * get terminal type.
29427983Sminshall 	 */
29527983Sminshall 	getterminaltype();
29627983Sminshall 
2979244Ssam 	if ((i = fork()) < 0)
2989244Ssam 		fatalperror(f, "fork", errno);
2996002Sroot 	if (i)
3006002Sroot 		telnet(f, p);
3016002Sroot 	close(f);
3026002Sroot 	close(p);
3036002Sroot 	dup2(t, 0);
3046002Sroot 	dup2(t, 1);
3056002Sroot 	dup2(t, 2);
3066002Sroot 	close(t);
30727983Sminshall 	envinit[0] = terminaltype;
30827983Sminshall 	envinit[1] = 0;
30913799Ssam 	environ = envinit;
31027649Sminshall 	/*
31127649Sminshall 	 * -h : pass on name of host.
31227983Sminshall 	 *		WARNING:  -h is accepted by login if and only if
31327983Sminshall 	 *			getuid() == 0.
31427649Sminshall 	 * -p : don't clobber the environment (so terminal type stays set).
31527649Sminshall 	 */
31627649Sminshall 	execl("/bin/login", "login", "-h", host,
31727983Sminshall 					terminaltype ? "-p" : 0, 0);
3189244Ssam 	fatalperror(f, "/bin/login", errno);
3199244Ssam 	/*NOTREACHED*/
3209244Ssam }
3219244Ssam 
3229244Ssam fatal(f, msg)
3239244Ssam 	int f;
3249244Ssam 	char *msg;
3259244Ssam {
3269244Ssam 	char buf[BUFSIZ];
3279244Ssam 
32817583Ssam 	(void) sprintf(buf, "telnetd: %s.\r\n", msg);
3299244Ssam 	(void) write(f, buf, strlen(buf));
3306002Sroot 	exit(1);
3316002Sroot }
3326002Sroot 
3339244Ssam fatalperror(f, msg, errno)
3349244Ssam 	int f;
3359244Ssam 	char *msg;
3369244Ssam 	int errno;
3379244Ssam {
3389244Ssam 	char buf[BUFSIZ];
3399244Ssam 	extern char *sys_errlist[];
3409244Ssam 
34117583Ssam 	(void) sprintf(buf, "%s: %s\r\n", msg, sys_errlist[errno]);
3429244Ssam 	fatal(f, buf);
3439244Ssam }
3449244Ssam 
34527185Sminshall 
3466002Sroot /*
34727185Sminshall  * Check a descriptor to see if out of band data exists on it.
34827185Sminshall  */
34927185Sminshall 
35027185Sminshall 
35127185Sminshall stilloob(s)
35227185Sminshall int	s;		/* socket number */
35327185Sminshall {
35427185Sminshall     static struct timeval timeout = { 0 };
35527185Sminshall     fd_set	excepts;
35627185Sminshall     int value;
35727185Sminshall 
35827185Sminshall     do {
35927185Sminshall 	FD_ZERO(&excepts);
36027185Sminshall 	FD_SET(s, &excepts);
36127185Sminshall 	value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout);
36227898Skarels     } while ((value == -1) && (errno == EINTR));
36327185Sminshall 
36427185Sminshall     if (value < 0) {
36527185Sminshall 	fatalperror(pty, "select", errno);
36627185Sminshall     }
36727185Sminshall     if (FD_ISSET(s, &excepts)) {
36827185Sminshall 	return 1;
36927185Sminshall     } else {
37027185Sminshall 	return 0;
37127185Sminshall     }
37227185Sminshall }
37327185Sminshall 
37427185Sminshall /*
3756002Sroot  * Main loop.  Select from pty and network, and
3766002Sroot  * hand data to telnet receiver finite state machine.
3776002Sroot  */
3786002Sroot telnet(f, p)
3796002Sroot {
3806002Sroot 	int on = 1;
38127898Skarels 	char hostname[MAXHOSTNAMELEN];
3826002Sroot 
3836002Sroot 	ioctl(f, FIONBIO, &on);
3846002Sroot 	ioctl(p, FIONBIO, &on);
38527649Sminshall #if	defined(SO_OOBINLINE)
38627649Sminshall 	setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on);
38727649Sminshall #endif	/* defined(SO_OOBINLINE) */
3886002Sroot 	signal(SIGTSTP, SIG_IGN);
38913028Ssam 	signal(SIGCHLD, cleanup);
39026083Slepreau 	setpgrp(0, 0);
3916002Sroot 
3928379Ssam 	/*
39327185Sminshall 	 * Request to do remote echo and to suppress go ahead.
3948379Ssam 	 */
39527983Sminshall 	if (!myopts[TELOPT_ECHO]) {
39627983Sminshall 	    dooption(TELOPT_ECHO);
39727983Sminshall 	}
39827983Sminshall 	if (!myopts[TELOPT_SGA]) {
39927983Sminshall 	    dooption(TELOPT_SGA);
40027983Sminshall 	}
40112713Ssam 	/*
40227649Sminshall 	 * Is the client side a 4.2 (NOT 4.3) system?  We need to know this
40327649Sminshall 	 * because 4.2 clients are unable to deal with TCP urgent data.
40427649Sminshall 	 *
40527649Sminshall 	 * To find out, we send out a "DO ECHO".  If the remote system
40627649Sminshall 	 * answers "WILL ECHO" it is probably a 4.2 client, and we note
40727649Sminshall 	 * that fact ("WILL ECHO" ==> that the client will echo what
40827649Sminshall 	 * WE, the server, sends it; it does NOT mean that the client will
40927649Sminshall 	 * echo the terminal input).
41027649Sminshall 	 */
41127649Sminshall 	sprintf(nfrontp, doopt, TELOPT_ECHO);
41227649Sminshall 	nfrontp += sizeof doopt-2;
41327983Sminshall 	hisopts[TELOPT_ECHO] = OPT_YES_BUT_ALWAYS_LOOK;
41427649Sminshall 
41527649Sminshall 	/*
41612713Ssam 	 * Show banner that getty never gave.
41727797Sminshall 	 *
41827797Sminshall 	 * The banner includes some null's (for TELNET CR disambiguation),
41927797Sminshall 	 * so we have to be somewhat complicated.
42012713Ssam 	 */
42127797Sminshall 
42212713Ssam 	gethostname(hostname, sizeof (hostname));
42327649Sminshall 
42427797Sminshall 	bcopy(BANNER1, nfrontp, sizeof BANNER1 -1);
42527797Sminshall 	nfrontp += sizeof BANNER1 - 1;
42627797Sminshall 	bcopy(hostname, nfrontp, strlen(hostname));
42727797Sminshall 	nfrontp += strlen(hostname);
42827797Sminshall 	bcopy(BANNER2, nfrontp, sizeof BANNER2 -1);
42927797Sminshall 	nfrontp += sizeof BANNER2 - 1;
43027797Sminshall 
43127649Sminshall 	/*
43227649Sminshall 	 * Call telrcv() once to pick up anything received during
43327649Sminshall 	 * terminal type negotiation.
43427649Sminshall 	 */
43527649Sminshall 	telrcv();
43627649Sminshall 
4376002Sroot 	for (;;) {
43827185Sminshall 		fd_set ibits, obits, xbits;
4396002Sroot 		register int c;
4406002Sroot 
44127185Sminshall 		if (ncc < 0 && pcc < 0)
44227185Sminshall 			break;
44327185Sminshall 
44427185Sminshall 		FD_ZERO(&ibits);
44527185Sminshall 		FD_ZERO(&obits);
44627185Sminshall 		FD_ZERO(&xbits);
4476002Sroot 		/*
4486002Sroot 		 * Never look for input if there's still
4496002Sroot 		 * stuff in the corresponding output buffer
4506002Sroot 		 */
45127185Sminshall 		if (nfrontp - nbackp || pcc > 0) {
45227185Sminshall 			FD_SET(f, &obits);
45327185Sminshall 		} else {
45427185Sminshall 			FD_SET(p, &ibits);
45527185Sminshall 		}
45627185Sminshall 		if (pfrontp - pbackp || ncc > 0) {
45727185Sminshall 			FD_SET(p, &obits);
45827185Sminshall 		} else {
45927185Sminshall 			FD_SET(f, &ibits);
46027185Sminshall 		}
46127185Sminshall 		if (!SYNCHing) {
46227185Sminshall 			FD_SET(f, &xbits);
46327185Sminshall 		}
46427185Sminshall 		if ((c = select(16, &ibits, &obits, &xbits,
46527185Sminshall 						(struct timeval *)0)) < 1) {
46627185Sminshall 			if (c == -1) {
46727185Sminshall 				if (errno == EINTR) {
46827185Sminshall 					continue;
46927185Sminshall 				}
47027185Sminshall 			}
4716002Sroot 			sleep(5);
4726002Sroot 			continue;
4736002Sroot 		}
4746002Sroot 
4756002Sroot 		/*
47627185Sminshall 		 * Any urgent data?
47727185Sminshall 		 */
47827185Sminshall 		if (FD_ISSET(net, &xbits)) {
47927185Sminshall 		    SYNCHing = 1;
48027185Sminshall 		}
48127185Sminshall 
48227185Sminshall 		/*
4836002Sroot 		 * Something to read from the network...
4846002Sroot 		 */
48527185Sminshall 		if (FD_ISSET(net, &ibits)) {
48627649Sminshall #if	!defined(SO_OOBINLINE)
48727185Sminshall 			/*
48827898Skarels 			 * In 4.2 (and 4.3 beta) systems, the
48927185Sminshall 			 * OOB indication and data handling in the kernel
49027185Sminshall 			 * is such that if two separate TCP Urgent requests
49127185Sminshall 			 * come in, one byte of TCP data will be overlaid.
49227185Sminshall 			 * This is fatal for Telnet, but we try to live
49327185Sminshall 			 * with it.
49427185Sminshall 			 *
49527185Sminshall 			 * In addition, in 4.2 (and...), a special protocol
49627185Sminshall 			 * is needed to pick up the TCP Urgent data in
49727185Sminshall 			 * the correct sequence.
49827185Sminshall 			 *
49927185Sminshall 			 * What we do is:  if we think we are in urgent
50027185Sminshall 			 * mode, we look to see if we are "at the mark".
50127185Sminshall 			 * If we are, we do an OOB receive.  If we run
50227185Sminshall 			 * this twice, we will do the OOB receive twice,
50327185Sminshall 			 * but the second will fail, since the second
50427185Sminshall 			 * time we were "at the mark", but there wasn't
50527185Sminshall 			 * any data there (the kernel doesn't reset
50627185Sminshall 			 * "at the mark" until we do a normal read).
50727185Sminshall 			 * Once we've read the OOB data, we go ahead
50827185Sminshall 			 * and do normal reads.
50927185Sminshall 			 *
51027185Sminshall 			 * There is also another problem, which is that
51127185Sminshall 			 * since the OOB byte we read doesn't put us
51227185Sminshall 			 * out of OOB state, and since that byte is most
51327185Sminshall 			 * likely the TELNET DM (data mark), we would
51427185Sminshall 			 * stay in the TELNET SYNCH (SYNCHing) state.
51527185Sminshall 			 * So, clocks to the rescue.  If we've "just"
51627185Sminshall 			 * received a DM, then we test for the
51727185Sminshall 			 * presence of OOB data when the receive OOB
51827185Sminshall 			 * fails (and AFTER we did the normal mode read
51927185Sminshall 			 * to clear "at the mark").
52027185Sminshall 			 */
52127185Sminshall 		    if (SYNCHing) {
52227185Sminshall 			int atmark;
52327185Sminshall 
52427185Sminshall 			ioctl(net, SIOCATMARK, (char *)&atmark);
52527185Sminshall 			if (atmark) {
52627185Sminshall 			    ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB);
52727185Sminshall 			    if ((ncc == -1) && (errno == EINVAL)) {
52827185Sminshall 				ncc = read(net, netibuf, sizeof (netibuf));
52927983Sminshall 				if (sequenceIs(didnetreceive, gotDM)) {
53027185Sminshall 				    SYNCHing = stilloob(net);
53127185Sminshall 				}
53227185Sminshall 			    }
53327185Sminshall 			} else {
53427185Sminshall 			    ncc = read(net, netibuf, sizeof (netibuf));
5356002Sroot 			}
53627185Sminshall 		    } else {
53727185Sminshall 			ncc = read(net, netibuf, sizeof (netibuf));
53827185Sminshall 		    }
53927185Sminshall 		    settimer(didnetreceive);
54027649Sminshall #else	/* !defined(SO_OOBINLINE)) */
54127185Sminshall 		    ncc = read(net, netibuf, sizeof (netibuf));
54227649Sminshall #endif	/* !defined(SO_OOBINLINE)) */
54327185Sminshall 		    if (ncc < 0 && errno == EWOULDBLOCK)
54427185Sminshall 			ncc = 0;
54527185Sminshall 		    else {
54627185Sminshall 			if (ncc <= 0) {
54727185Sminshall 			    break;
54827185Sminshall 			}
54927185Sminshall 			netip = netibuf;
55027185Sminshall 		    }
5516002Sroot 		}
5526002Sroot 
5536002Sroot 		/*
5546002Sroot 		 * Something to read from the pty...
5556002Sroot 		 */
55627185Sminshall 		if (FD_ISSET(p, &ibits)) {
5576002Sroot 			pcc = read(p, ptyibuf, BUFSIZ);
5586002Sroot 			if (pcc < 0 && errno == EWOULDBLOCK)
5596002Sroot 				pcc = 0;
5606002Sroot 			else {
5616002Sroot 				if (pcc <= 0)
5626002Sroot 					break;
5636002Sroot 				ptyip = ptyibuf;
5646002Sroot 			}
5656002Sroot 		}
5666002Sroot 
5676002Sroot 		while (pcc > 0) {
5686002Sroot 			if ((&netobuf[BUFSIZ] - nfrontp) < 2)
5696002Sroot 				break;
5706002Sroot 			c = *ptyip++ & 0377, pcc--;
5716002Sroot 			if (c == IAC)
5726002Sroot 				*nfrontp++ = c;
5736002Sroot 			*nfrontp++ = c;
57431940Sbostic 			/* Don't do CR-NUL if we are in binary mode */
57531940Sbostic 			if ((c == '\r') && (myopts[TELOPT_BINARY] == OPT_NO)) {
57627020Sminshall 				if (pcc > 0 && ((*ptyip & 0377) == '\n')) {
57727020Sminshall 					*nfrontp++ = *ptyip++ & 0377;
57827020Sminshall 					pcc--;
57927020Sminshall 				} else
58027020Sminshall 					*nfrontp++ = '\0';
58127020Sminshall 			}
5826002Sroot 		}
58327185Sminshall 		if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0)
5846002Sroot 			netflush();
5856002Sroot 		if (ncc > 0)
5866002Sroot 			telrcv();
58727185Sminshall 		if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0)
5886002Sroot 			ptyflush();
5896002Sroot 	}
5906002Sroot 	cleanup();
5916002Sroot }
5926002Sroot 
5936002Sroot /*
5946002Sroot  * State for recv fsm
5956002Sroot  */
5966002Sroot #define	TS_DATA		0	/* base state */
5976002Sroot #define	TS_IAC		1	/* look for double IAC's */
5986002Sroot #define	TS_CR		2	/* CR-LF ->'s CR */
59927649Sminshall #define	TS_SB		3	/* throw away begin's... */
60027649Sminshall #define	TS_SE		4	/* ...end's (suboption negotiation) */
6016002Sroot #define	TS_WILL		5	/* will option negotiation */
6026002Sroot #define	TS_WONT		6	/* wont " */
6036002Sroot #define	TS_DO		7	/* do " */
6046002Sroot #define	TS_DONT		8	/* dont " */
6056002Sroot 
6066002Sroot telrcv()
6076002Sroot {
6086002Sroot 	register int c;
6096002Sroot 	static int state = TS_DATA;
6106002Sroot 
6116002Sroot 	while (ncc > 0) {
6126002Sroot 		if ((&ptyobuf[BUFSIZ] - pfrontp) < 2)
6136002Sroot 			return;
6146002Sroot 		c = *netip++ & 0377, ncc--;
6156002Sroot 		switch (state) {
6166002Sroot 
61726090Sminshall 		case TS_CR:
61826090Sminshall 			state = TS_DATA;
619*32097Sminshall 			/* Strip off \n or \0 after a \r */
62026499Sminshall 			if ((c == 0) || (c == '\n')) {
62126090Sminshall 				break;
62226499Sminshall 			}
62326090Sminshall 			/* FALL THROUGH */
62426090Sminshall 
6256002Sroot 		case TS_DATA:
6266002Sroot 			if (c == IAC) {
6276002Sroot 				state = TS_IAC;
6286002Sroot 				break;
6296002Sroot 			}
6306002Sroot 			if (inter > 0)
6316002Sroot 				break;
63227020Sminshall 			/*
633*32097Sminshall 			 * We now map \r\n ==> \r for pragmatic reasons.
634*32097Sminshall 			 * Many client implementations send \r\n when
635*32097Sminshall 			 * the user hits the CarriageReturn key.
636*32097Sminshall 			 *
637*32097Sminshall 			 * We USED to map \r\n ==> \n, since \r\n says
63827020Sminshall 			 * that we want to be in column 1 of the next
63927020Sminshall 			 * printable line, and \n is the standard
64027020Sminshall 			 * unix way of saying that (\r is only good
64127020Sminshall 			 * if CRMOD is set, which it normally is).
64227020Sminshall 			 */
64331940Sbostic 			if ((c == '\r') && (hisopts[TELOPT_BINARY] == OPT_NO)) {
644*32097Sminshall 				state = TS_CR;
64526499Sminshall 			}
64626499Sminshall 			*pfrontp++ = c;
6476002Sroot 			break;
6486002Sroot 
6496002Sroot 		case TS_IAC:
6506002Sroot 			switch (c) {
6516002Sroot 
6526002Sroot 			/*
6536002Sroot 			 * Send the process on the pty side an
6546002Sroot 			 * interrupt.  Do this with a NULL or
6556002Sroot 			 * interrupt char; depending on the tty mode.
6566002Sroot 			 */
6576002Sroot 			case IP:
6586002Sroot 				interrupt();
6596002Sroot 				break;
6606002Sroot 
66127229Sminshall 			case BREAK:
66227229Sminshall 				sendbrk();
66327229Sminshall 				break;
66427229Sminshall 
6656002Sroot 			/*
6666002Sroot 			 * Are You There?
6676002Sroot 			 */
6686002Sroot 			case AYT:
66917583Ssam 				strcpy(nfrontp, "\r\n[Yes]\r\n");
67017583Ssam 				nfrontp += 9;
6716002Sroot 				break;
6726002Sroot 
6736002Sroot 			/*
67427185Sminshall 			 * Abort Output
67527185Sminshall 			 */
67627185Sminshall 			case AO: {
67727185Sminshall 					struct ltchars tmpltc;
67827185Sminshall 
67927185Sminshall 					ptyflush();	/* half-hearted */
68027185Sminshall 					ioctl(pty, TIOCGLTC, &tmpltc);
68127185Sminshall 					if (tmpltc.t_flushc != '\377') {
68227185Sminshall 						*pfrontp++ = tmpltc.t_flushc;
68327185Sminshall 					}
68427260Sminshall 					netclear();	/* clear buffer back */
68527185Sminshall 					*nfrontp++ = IAC;
68627185Sminshall 					*nfrontp++ = DM;
68727187Sminshall 					neturg = nfrontp-1; /* off by one XXX */
68827185Sminshall 					break;
68927185Sminshall 				}
69027185Sminshall 
69127185Sminshall 			/*
6926002Sroot 			 * Erase Character and
6936002Sroot 			 * Erase Line
6946002Sroot 			 */
6956002Sroot 			case EC:
69627185Sminshall 			case EL: {
69727185Sminshall 					struct sgttyb b;
69827185Sminshall 					char ch;
6996002Sroot 
70027185Sminshall 					ptyflush();	/* half-hearted */
70127185Sminshall 					ioctl(pty, TIOCGETP, &b);
70227185Sminshall 					ch = (c == EC) ?
70327185Sminshall 						b.sg_erase : b.sg_kill;
70427185Sminshall 					if (ch != '\377') {
70527185Sminshall 						*pfrontp++ = ch;
70627185Sminshall 					}
70727185Sminshall 					break;
70827185Sminshall 				}
70927185Sminshall 
7106002Sroot 			/*
7116002Sroot 			 * Check for urgent data...
7126002Sroot 			 */
7136002Sroot 			case DM:
71427185Sminshall 				SYNCHing = stilloob(net);
71527185Sminshall 				settimer(gotDM);
7166002Sroot 				break;
7176002Sroot 
71827185Sminshall 
7196002Sroot 			/*
7206002Sroot 			 * Begin option subnegotiation...
7216002Sroot 			 */
7226002Sroot 			case SB:
72327649Sminshall 				state = TS_SB;
7246002Sroot 				continue;
7256002Sroot 
7266002Sroot 			case WILL:
72727188Sminshall 				state = TS_WILL;
72827188Sminshall 				continue;
72927188Sminshall 
7306002Sroot 			case WONT:
73127188Sminshall 				state = TS_WONT;
73227188Sminshall 				continue;
73327188Sminshall 
7346002Sroot 			case DO:
73527188Sminshall 				state = TS_DO;
73627188Sminshall 				continue;
73727188Sminshall 
7386002Sroot 			case DONT:
73927188Sminshall 				state = TS_DONT;
7406002Sroot 				continue;
7416002Sroot 
7426002Sroot 			case IAC:
7436002Sroot 				*pfrontp++ = c;
7446002Sroot 				break;
7456002Sroot 			}
7466002Sroot 			state = TS_DATA;
7476002Sroot 			break;
7486002Sroot 
74927649Sminshall 		case TS_SB:
75027649Sminshall 			if (c == IAC) {
75127649Sminshall 				state = TS_SE;
75227649Sminshall 			} else {
75327649Sminshall 				SB_ACCUM(c);
75427649Sminshall 			}
7556002Sroot 			break;
7566002Sroot 
75727649Sminshall 		case TS_SE:
75827649Sminshall 			if (c != SE) {
75927649Sminshall 				if (c != IAC) {
76027649Sminshall 					SB_ACCUM(IAC);
76127649Sminshall 				}
76227649Sminshall 				SB_ACCUM(c);
76327649Sminshall 				state = TS_SB;
76427649Sminshall 			} else {
76527649Sminshall 				SB_TERM();
76627649Sminshall 				suboption();	/* handle sub-option */
76727649Sminshall 				state = TS_DATA;
76827649Sminshall 			}
7696002Sroot 			break;
7706002Sroot 
7716002Sroot 		case TS_WILL:
77227983Sminshall 			if (hisopts[c] != OPT_YES)
7736002Sroot 				willoption(c);
7746002Sroot 			state = TS_DATA;
7756002Sroot 			continue;
7766002Sroot 
7776002Sroot 		case TS_WONT:
77827983Sminshall 			if (hisopts[c] != OPT_NO)
7796002Sroot 				wontoption(c);
7806002Sroot 			state = TS_DATA;
7816002Sroot 			continue;
7826002Sroot 
7836002Sroot 		case TS_DO:
78427983Sminshall 			if (myopts[c] != OPT_YES)
7856002Sroot 				dooption(c);
7866002Sroot 			state = TS_DATA;
7876002Sroot 			continue;
7886002Sroot 
7896002Sroot 		case TS_DONT:
79027983Sminshall 			if (myopts[c] != OPT_NO) {
79127649Sminshall 				dontoption(c);
7926002Sroot 			}
7936002Sroot 			state = TS_DATA;
7946002Sroot 			continue;
7956002Sroot 
7966002Sroot 		default:
79727898Skarels 			syslog(LOG_ERR, "telnetd: panic state=%d\n", state);
7989218Ssam 			printf("telnetd: panic state=%d\n", state);
7996002Sroot 			exit(1);
8006002Sroot 		}
8016002Sroot 	}
8026002Sroot }
8036002Sroot 
8046002Sroot willoption(option)
8056002Sroot 	int option;
8066002Sroot {
8076002Sroot 	char *fmt;
8086002Sroot 
8096002Sroot 	switch (option) {
8106002Sroot 
8116002Sroot 	case TELOPT_BINARY:
8126002Sroot 		mode(RAW, 0);
81327188Sminshall 		fmt = doopt;
81427188Sminshall 		break;
8156002Sroot 
8166002Sroot 	case TELOPT_ECHO:
81727649Sminshall 		not42 = 0;		/* looks like a 4.2 system */
81827649Sminshall 		/*
81927649Sminshall 		 * Now, in a 4.2 system, to break them out of ECHOing
82027649Sminshall 		 * (to the terminal) mode, we need to send a "WILL ECHO".
82127649Sminshall 		 * Kludge upon kludge!
82227649Sminshall 		 */
82327983Sminshall 		if (myopts[TELOPT_ECHO] == OPT_YES) {
82427649Sminshall 		    dooption(TELOPT_ECHO);
82527649Sminshall 		}
82627649Sminshall 		fmt = dont;
82727188Sminshall 		break;
8286002Sroot 
82927649Sminshall 	case TELOPT_TTYPE:
83027983Sminshall 		settimer(ttypeopt);
83127983Sminshall 		if (hisopts[TELOPT_TTYPE] == OPT_YES_BUT_ALWAYS_LOOK) {
83227983Sminshall 		    hisopts[TELOPT_TTYPE] = OPT_YES;
83327983Sminshall 		    return;
83427983Sminshall 		}
83527983Sminshall 		fmt = doopt;
83627983Sminshall 		break;
83727983Sminshall 
8386002Sroot 	case TELOPT_SGA:
8396002Sroot 		fmt = doopt;
8406002Sroot 		break;
8416002Sroot 
8426002Sroot 	case TELOPT_TM:
8436002Sroot 		fmt = dont;
8446002Sroot 		break;
8456002Sroot 
8466002Sroot 	default:
8476002Sroot 		fmt = dont;
8486002Sroot 		break;
8496002Sroot 	}
85027188Sminshall 	if (fmt == doopt) {
85127983Sminshall 		hisopts[option] = OPT_YES;
85227188Sminshall 	} else {
85327983Sminshall 		hisopts[option] = OPT_NO;
85427188Sminshall 	}
8556023Ssam 	sprintf(nfrontp, fmt, option);
8568379Ssam 	nfrontp += sizeof (dont) - 2;
8576002Sroot }
8586002Sroot 
8596002Sroot wontoption(option)
8606002Sroot 	int option;
8616002Sroot {
8626002Sroot 	char *fmt;
8636002Sroot 
8646002Sroot 	switch (option) {
8656002Sroot 	case TELOPT_ECHO:
86627649Sminshall 		not42 = 1;		/* doesn't seem to be a 4.2 system */
86727188Sminshall 		break;
8686002Sroot 
8696002Sroot 	case TELOPT_BINARY:
8706002Sroot 		mode(0, RAW);
8716002Sroot 		break;
87228044Sminshall 
87328044Sminshall 	case TELOPT_TTYPE:
87428044Sminshall 	    settimer(ttypeopt);
87528044Sminshall 	    break;
8766002Sroot 	}
87728044Sminshall 
87827188Sminshall 	fmt = dont;
87927983Sminshall 	hisopts[option] = OPT_NO;
8806002Sroot 	sprintf(nfrontp, fmt, option);
8818379Ssam 	nfrontp += sizeof (doopt) - 2;
8826002Sroot }
8836002Sroot 
8846002Sroot dooption(option)
8856002Sroot 	int option;
8866002Sroot {
8876002Sroot 	char *fmt;
8886002Sroot 
8896002Sroot 	switch (option) {
8906002Sroot 
8916002Sroot 	case TELOPT_TM:
8926002Sroot 		fmt = wont;
8936002Sroot 		break;
8946002Sroot 
8956002Sroot 	case TELOPT_ECHO:
8966002Sroot 		mode(ECHO|CRMOD, 0);
89727188Sminshall 		fmt = will;
89827188Sminshall 		break;
8996002Sroot 
9006002Sroot 	case TELOPT_BINARY:
9016002Sroot 		mode(RAW, 0);
90227188Sminshall 		fmt = will;
90327188Sminshall 		break;
9046002Sroot 
9056002Sroot 	case TELOPT_SGA:
9066002Sroot 		fmt = will;
9076002Sroot 		break;
9086002Sroot 
9096002Sroot 	default:
9106002Sroot 		fmt = wont;
9116002Sroot 		break;
9126002Sroot 	}
91327188Sminshall 	if (fmt == will) {
91427983Sminshall 	    myopts[option] = OPT_YES;
91527188Sminshall 	} else {
91627983Sminshall 	    myopts[option] = OPT_NO;
91727188Sminshall 	}
9186002Sroot 	sprintf(nfrontp, fmt, option);
9198379Ssam 	nfrontp += sizeof (doopt) - 2;
9206002Sroot }
9216002Sroot 
92227649Sminshall 
92327649Sminshall dontoption(option)
92427649Sminshall int option;
92527649Sminshall {
92627649Sminshall     char *fmt;
92727649Sminshall 
92827649Sminshall     switch (option) {
92927649Sminshall     case TELOPT_ECHO:		/* we should stop echoing */
93027649Sminshall 	mode(0, ECHO|CRMOD);
93127649Sminshall 	fmt = wont;
93227649Sminshall 	break;
93327983Sminshall 
93427649Sminshall     default:
93527649Sminshall 	fmt = wont;
93627649Sminshall 	break;
93727649Sminshall     }
93827983Sminshall 
93927649Sminshall     if (fmt = wont) {
94027983Sminshall 	myopts[option] = OPT_NO;
94127649Sminshall     } else {
94227983Sminshall 	myopts[option] = OPT_YES;
94327649Sminshall     }
94427649Sminshall     sprintf(nfrontp, fmt, option);
94527649Sminshall     nfrontp += sizeof (wont) - 2;
94627649Sminshall }
94727649Sminshall 
94827649Sminshall /*
94927649Sminshall  * suboption()
95027649Sminshall  *
95127649Sminshall  *	Look at the sub-option buffer, and try to be helpful to the other
95227649Sminshall  * side.
95327649Sminshall  *
95427649Sminshall  *	Currently we recognize:
95527649Sminshall  *
95627983Sminshall  *	Terminal type is
95727649Sminshall  */
95827649Sminshall 
95927649Sminshall suboption()
96027649Sminshall {
96127983Sminshall     switch (SB_GET()) {
96227983Sminshall     case TELOPT_TTYPE: {		/* Yaaaay! */
96327983Sminshall 	static char terminalname[5+41] = "TERM=";
96427983Sminshall 
96527983Sminshall 	settimer(ttypesubopt);
96627983Sminshall 
96727983Sminshall 	if (SB_GET() != TELQUAL_IS) {
96827983Sminshall 	    return;		/* ??? XXX but, this is the most robust */
96927983Sminshall 	}
97027983Sminshall 
97127983Sminshall 	terminaltype = terminalname+strlen(terminalname);
97227983Sminshall 
97327983Sminshall 	while ((terminaltype < (terminalname + sizeof terminalname-1)) &&
97427983Sminshall 								    !SB_EOF()) {
97527983Sminshall 	    register int c;
97627983Sminshall 
97727983Sminshall 	    c = SB_GET();
97827983Sminshall 	    if (isupper(c)) {
97927983Sminshall 		c = tolower(c);
98027983Sminshall 	    }
98127983Sminshall 	    *terminaltype++ = c;    /* accumulate name */
98227983Sminshall 	}
98327983Sminshall 	*terminaltype = 0;
98427983Sminshall 	terminaltype = terminalname;
98527983Sminshall 	break;
98627983Sminshall     }
98727983Sminshall 
98827649Sminshall     default:
98927649Sminshall 	;
99027649Sminshall     }
99127649Sminshall }
99227649Sminshall 
9936002Sroot mode(on, off)
9946002Sroot 	int on, off;
9956002Sroot {
9966002Sroot 	struct sgttyb b;
9976002Sroot 
9986002Sroot 	ptyflush();
9996002Sroot 	ioctl(pty, TIOCGETP, &b);
10006002Sroot 	b.sg_flags |= on;
10016002Sroot 	b.sg_flags &= ~off;
10026002Sroot 	ioctl(pty, TIOCSETP, &b);
10036002Sroot }
10046002Sroot 
10056002Sroot /*
10066002Sroot  * Send interrupt to process on other side of pty.
10076002Sroot  * If it is in raw mode, just write NULL;
10086002Sroot  * otherwise, write intr char.
10096002Sroot  */
10106002Sroot interrupt()
10116002Sroot {
10126002Sroot 	struct sgttyb b;
10136002Sroot 	struct tchars tchars;
10146002Sroot 
10156002Sroot 	ptyflush();	/* half-hearted */
10166002Sroot 	ioctl(pty, TIOCGETP, &b);
10176002Sroot 	if (b.sg_flags & RAW) {
10186002Sroot 		*pfrontp++ = '\0';
10196002Sroot 		return;
10206002Sroot 	}
10216002Sroot 	*pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ?
10226002Sroot 		'\177' : tchars.t_intrc;
10236002Sroot }
10246002Sroot 
102527229Sminshall /*
102627229Sminshall  * Send quit to process on other side of pty.
102727229Sminshall  * If it is in raw mode, just write NULL;
102827229Sminshall  * otherwise, write quit char.
102927229Sminshall  */
103027229Sminshall sendbrk()
103127229Sminshall {
103227229Sminshall 	struct sgttyb b;
103327229Sminshall 	struct tchars tchars;
103427229Sminshall 
103527229Sminshall 	ptyflush();	/* half-hearted */
103627229Sminshall 	ioctl(pty, TIOCGETP, &b);
103727229Sminshall 	if (b.sg_flags & RAW) {
103827229Sminshall 		*pfrontp++ = '\0';
103927229Sminshall 		return;
104027229Sminshall 	}
104127229Sminshall 	*pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ?
104227229Sminshall 		'\034' : tchars.t_quitc;
104327229Sminshall }
104427229Sminshall 
10456002Sroot ptyflush()
10466002Sroot {
10476002Sroot 	int n;
10486002Sroot 
10496002Sroot 	if ((n = pfrontp - pbackp) > 0)
10506002Sroot 		n = write(pty, pbackp, n);
10518346Ssam 	if (n < 0)
10528346Ssam 		return;
10536002Sroot 	pbackp += n;
10546002Sroot 	if (pbackp == pfrontp)
10556002Sroot 		pbackp = pfrontp = ptyobuf;
10566002Sroot }
105727260Sminshall 
105827260Sminshall /*
105927260Sminshall  * nextitem()
106027260Sminshall  *
106127260Sminshall  *	Return the address of the next "item" in the TELNET data
106227260Sminshall  * stream.  This will be the address of the next character if
106327260Sminshall  * the current address is a user data character, or it will
106427260Sminshall  * be the address of the character following the TELNET command
106527260Sminshall  * if the current address is a TELNET IAC ("I Am a Command")
106627260Sminshall  * character.
106727260Sminshall  */
10686002Sroot 
106927260Sminshall char *
107027260Sminshall nextitem(current)
107127260Sminshall char	*current;
10726002Sroot {
107327260Sminshall     if ((*current&0xff) != IAC) {
107427260Sminshall 	return current+1;
107527260Sminshall     }
107627260Sminshall     switch (*(current+1)&0xff) {
107727260Sminshall     case DO:
107827260Sminshall     case DONT:
107927260Sminshall     case WILL:
108027260Sminshall     case WONT:
108127260Sminshall 	return current+3;
108227260Sminshall     case SB:		/* loop forever looking for the SE */
108327260Sminshall 	{
108427260Sminshall 	    register char *look = current+2;
10856002Sroot 
108627260Sminshall 	    for (;;) {
108727260Sminshall 		if ((*look++&0xff) == IAC) {
108827260Sminshall 		    if ((*look++&0xff) == SE) {
108927260Sminshall 			return look;
109027260Sminshall 		    }
109127260Sminshall 		}
109227260Sminshall 	    }
10938346Ssam 	}
109427260Sminshall     default:
109527260Sminshall 	return current+2;
109627260Sminshall     }
10976002Sroot }
10986002Sroot 
109927185Sminshall 
110027185Sminshall /*
110127260Sminshall  * netclear()
110227260Sminshall  *
110327260Sminshall  *	We are about to do a TELNET SYNCH operation.  Clear
110427260Sminshall  * the path to the network.
110527260Sminshall  *
110627260Sminshall  *	Things are a bit tricky since we may have sent the first
110727260Sminshall  * byte or so of a previous TELNET command into the network.
110827260Sminshall  * So, we have to scan the network buffer from the beginning
110927260Sminshall  * until we are up to where we want to be.
111027260Sminshall  *
111127260Sminshall  *	A side effect of what we do, just to keep things
111227260Sminshall  * simple, is to clear the urgent data pointer.  The principal
111327260Sminshall  * caller should be setting the urgent data pointer AFTER calling
111427260Sminshall  * us in any case.
111527260Sminshall  */
111627260Sminshall 
111727260Sminshall netclear()
111827260Sminshall {
111927260Sminshall     register char *thisitem, *next;
112027260Sminshall     char *good;
112127260Sminshall #define	wewant(p)	((nfrontp > p) && ((*p&0xff) == IAC) && \
112227260Sminshall 				((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
112327260Sminshall 
112427260Sminshall     thisitem = netobuf;
112527260Sminshall 
112627260Sminshall     while ((next = nextitem(thisitem)) <= nbackp) {
112727260Sminshall 	thisitem = next;
112827260Sminshall     }
112927260Sminshall 
113027260Sminshall     /* Now, thisitem is first before/at boundary. */
113127260Sminshall 
113227260Sminshall     good = netobuf;	/* where the good bytes go */
113327260Sminshall 
113427260Sminshall     while (nfrontp > thisitem) {
113527260Sminshall 	if (wewant(thisitem)) {
113627260Sminshall 	    int length;
113727260Sminshall 
113827260Sminshall 	    next = thisitem;
113927260Sminshall 	    do {
114027260Sminshall 		next = nextitem(next);
114127260Sminshall 	    } while (wewant(next) && (nfrontp > next));
114227260Sminshall 	    length = next-thisitem;
114327260Sminshall 	    bcopy(thisitem, good, length);
114427260Sminshall 	    good += length;
114527260Sminshall 	    thisitem = next;
114627260Sminshall 	} else {
114727260Sminshall 	    thisitem = nextitem(thisitem);
114827260Sminshall 	}
114927260Sminshall     }
115027260Sminshall 
115127260Sminshall     nbackp = netobuf;
115227260Sminshall     nfrontp = good;		/* next byte to be sent */
115327260Sminshall     neturg = 0;
115427260Sminshall }
115527260Sminshall 
115627260Sminshall /*
115727185Sminshall  *  netflush
115827185Sminshall  *		Send as much data as possible to the network,
115927185Sminshall  *	handling requests for urgent data.
116027185Sminshall  */
116127185Sminshall 
116227185Sminshall 
116327185Sminshall netflush()
116427185Sminshall {
116527185Sminshall     int n;
116627185Sminshall 
116727185Sminshall     if ((n = nfrontp - nbackp) > 0) {
116827649Sminshall 	/*
116927649Sminshall 	 * if no urgent data, or if the other side appears to be an
117027649Sminshall 	 * old 4.2 client (and thus unable to survive TCP urgent data),
117127649Sminshall 	 * write the entire buffer in non-OOB mode.
117227649Sminshall 	 */
117327649Sminshall 	if ((neturg == 0) || (not42 == 0)) {
117427185Sminshall 	    n = write(net, nbackp, n);	/* normal write */
117527185Sminshall 	} else {
117627185Sminshall 	    n = neturg - nbackp;
117727185Sminshall 	    /*
117827185Sminshall 	     * In 4.2 (and 4.3) systems, there is some question about
117927185Sminshall 	     * what byte in a sendOOB operation is the "OOB" data.
118027185Sminshall 	     * To make ourselves compatible, we only send ONE byte
118127185Sminshall 	     * out of band, the one WE THINK should be OOB (though
118227185Sminshall 	     * we really have more the TCP philosophy of urgent data
118327185Sminshall 	     * rather than the Unix philosophy of OOB data).
118427185Sminshall 	     */
118527185Sminshall 	    if (n > 1) {
118627185Sminshall 		n = send(net, nbackp, n-1, 0);	/* send URGENT all by itself */
118727185Sminshall 	    } else {
118827185Sminshall 		n = send(net, nbackp, n, MSG_OOB);	/* URGENT data */
118927185Sminshall 	    }
119027185Sminshall 	}
119127185Sminshall     }
119227185Sminshall     if (n < 0) {
119327185Sminshall 	if (errno == EWOULDBLOCK)
119427185Sminshall 	    return;
119527185Sminshall 	/* should blow this guy away... */
119627185Sminshall 	return;
119727185Sminshall     }
119827185Sminshall     nbackp += n;
119927185Sminshall     if (nbackp >= neturg) {
120027185Sminshall 	neturg = 0;
120127185Sminshall     }
120227185Sminshall     if (nbackp == nfrontp) {
120327185Sminshall 	nbackp = nfrontp = netobuf;
120427185Sminshall     }
120527185Sminshall }
120627185Sminshall 
12076002Sroot cleanup()
12086002Sroot {
12096002Sroot 
12106002Sroot 	rmut();
121110008Ssam 	vhangup();	/* XXX */
121210191Ssam 	shutdown(net, 2);
12136002Sroot 	exit(1);
12146002Sroot }
12156002Sroot 
12166002Sroot #include <utmp.h>
12176002Sroot 
12186002Sroot struct	utmp wtmp;
12196002Sroot char	wtmpf[]	= "/usr/adm/wtmp";
122023567Sbloom char	utmpf[] = "/etc/utmp";
122123567Sbloom #define SCPYN(a, b)	strncpy(a, b, sizeof(a))
122223567Sbloom #define SCMPN(a, b)	strncmp(a, b, sizeof(a))
12236002Sroot 
12246002Sroot rmut()
12256002Sroot {
12266002Sroot 	register f;
12276002Sroot 	int found = 0;
122823567Sbloom 	struct utmp *u, *utmp;
122923567Sbloom 	int nutmp;
123023567Sbloom 	struct stat statbf;
12316002Sroot 
123223567Sbloom 	f = open(utmpf, O_RDWR);
12336002Sroot 	if (f >= 0) {
123423567Sbloom 		fstat(f, &statbf);
123523567Sbloom 		utmp = (struct utmp *)malloc(statbf.st_size);
123623567Sbloom 		if (!utmp)
123723567Sbloom 			syslog(LOG_ERR, "utmp malloc failed");
123823567Sbloom 		if (statbf.st_size && utmp) {
123923567Sbloom 			nutmp = read(f, utmp, statbf.st_size);
124023567Sbloom 			nutmp /= sizeof(struct utmp);
124123567Sbloom 
124223567Sbloom 			for (u = utmp ; u < &utmp[nutmp] ; u++) {
124323567Sbloom 				if (SCMPN(u->ut_line, line+5) ||
124423567Sbloom 				    u->ut_name[0]==0)
124523567Sbloom 					continue;
124623567Sbloom 				lseek(f, ((long)u)-((long)utmp), L_SET);
124723567Sbloom 				SCPYN(u->ut_name, "");
124823567Sbloom 				SCPYN(u->ut_host, "");
124923567Sbloom 				time(&u->ut_time);
125023567Sbloom 				write(f, (char *)u, sizeof(wtmp));
125123567Sbloom 				found++;
125223567Sbloom 			}
12536002Sroot 		}
12546002Sroot 		close(f);
12556002Sroot 	}
12566002Sroot 	if (found) {
125717583Ssam 		f = open(wtmpf, O_WRONLY|O_APPEND);
12586002Sroot 		if (f >= 0) {
12596002Sroot 			SCPYN(wtmp.ut_line, line+5);
12606002Sroot 			SCPYN(wtmp.ut_name, "");
126112683Ssam 			SCPYN(wtmp.ut_host, "");
12626002Sroot 			time(&wtmp.ut_time);
126323567Sbloom 			write(f, (char *)&wtmp, sizeof(wtmp));
12646002Sroot 			close(f);
12656002Sroot 		}
12666002Sroot 	}
12676002Sroot 	chmod(line, 0666);
12686002Sroot 	chown(line, 0, 0);
12696002Sroot 	line[strlen("/dev/")] = 'p';
12706002Sroot 	chmod(line, 0666);
12716002Sroot 	chown(line, 0, 0);
12726002Sroot }
1273