xref: /csrg-svn/libexec/telnetd/telnetd.c (revision 27649)
121182Sdist /*
221182Sdist  * Copyright (c) 1983 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*27649Sminshall static char sccsid[] = "@(#)telnetd.c	5.14 (Berkeley) 04/30/86";
1521182Sdist #endif not lint
1621182Sdist 
176002Sroot /*
186002Sroot  * Stripped-down telnet server.
196002Sroot  */
209218Ssam #include <sys/types.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>
37*27649Sminshall #include <ctype.h>
389218Ssam 
3913798Ssam #define	BELL	'\07'
4023567Sbloom #define BANNER	"\r\n\r\n4.3 BSD UNIX (%s)\r\n\r\r\n\r%s"
416002Sroot 
42*27649Sminshall #define	OPT_DONT	0		/* don't do this option */
43*27649Sminshall #define	OPT_WONT	0		/* won't do this option */
44*27649Sminshall #define	OPT_DO		1		/* do this option */
45*27649Sminshall #define	OPT_WILL	1		/* will do this option */
46*27649Sminshall #define	OPT_ALWAYS_LOOK	2		/* special case for echo */
476002Sroot char	hisopts[256];
486002Sroot char	myopts[256];
496002Sroot 
506002Sroot char	doopt[] = { IAC, DO, '%', 'c', 0 };
516002Sroot char	dont[] = { IAC, DONT, '%', 'c', 0 };
526002Sroot char	will[] = { IAC, WILL, '%', 'c', 0 };
536002Sroot char	wont[] = { IAC, WONT, '%', 'c', 0 };
546002Sroot 
556002Sroot /*
566002Sroot  * I/O data buffers, pointers, and counters.
576002Sroot  */
586002Sroot char	ptyibuf[BUFSIZ], *ptyip = ptyibuf;
59*27649Sminshall 
606002Sroot char	ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf;
61*27649Sminshall 
626002Sroot char	netibuf[BUFSIZ], *netip = netibuf;
63*27649Sminshall #define	NIACCUM(c)	{   *netip++ = c; \
64*27649Sminshall 			    ncc++; \
65*27649Sminshall 			}
66*27649Sminshall 
676388Ssam char	netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf;
6827185Sminshall char	*neturg = 0;		/* one past last bye of urgent data */
69*27649Sminshall 	/* the remote system seems to NOT be an old 4.2 */
70*27649Sminshall int	not42 = 1;
71*27649Sminshall 
72*27649Sminshall 
73*27649Sminshall char	subbuffer[100], *subpointer, *subend;	/* buffer for sub-options */
74*27649Sminshall #define	SB_CLEAR()	subpointer = subbuffer;
75*27649Sminshall #define	SB_TERM()	subend = subpointer;
76*27649Sminshall #define	SB_ACCUM(c)	if (subpointer < (subbuffer+sizeof subbuffer)) { \
77*27649Sminshall 				*subpointer++ = (c); \
78*27649Sminshall 			}
79*27649Sminshall 
806002Sroot int	pcc, ncc;
816002Sroot 
826002Sroot int	pty, net;
836002Sroot int	inter;
8413799Ssam extern	char **environ;
856002Sroot extern	int errno;
8620188Skarels char	*line;
8727185Sminshall int	SYNCHing = 0;		/* we are in TELNET SYNCH mode */
8827185Sminshall /*
8927185Sminshall  * The following are some clocks used to decide how to interpret
9027185Sminshall  * the relationship between various variables.
9127185Sminshall  */
926002Sroot 
9327185Sminshall struct {
9427185Sminshall     int
9527185Sminshall 	system,			/* what the current time is */
9627185Sminshall 	echotoggle,		/* last time user entered echo character */
9727185Sminshall 	modenegotiated,		/* last time operating mode negotiated */
9827185Sminshall 	didnetreceive,		/* last time we read data from network */
9927185Sminshall 	gotDM;			/* when did we last see a data mark */
10027185Sminshall } clocks;
10127185Sminshall 
10227185Sminshall #define	settimer(x)	clocks.x = clocks.system++
10327185Sminshall 
1046002Sroot main(argc, argv)
1056002Sroot 	char *argv[];
1066002Sroot {
10716371Skarels 	struct sockaddr_in from;
10817156Ssam 	int on = 1, fromlen;
1096002Sroot 
11027185Sminshall #if	defined(DEBUG)
11127185Sminshall 	{
11227185Sminshall 	    int s, ns, foo;
11327185Sminshall 	    struct servent *sp;
11427185Sminshall 	    static struct sockaddr_in sin = { AF_INET };
11527185Sminshall 
11627185Sminshall 	    sp = getservbyname("telnet", "tcp");
11727185Sminshall 	    if (sp == 0) {
11827185Sminshall 		    fprintf(stderr, "telnetd: tcp/telnet: unknown service\n");
11927185Sminshall 		    exit(1);
12027185Sminshall 	    }
12127185Sminshall 	    sin.sin_port = sp->s_port;
12227185Sminshall 	    argc--, argv++;
12327185Sminshall 	    if (argc > 0) {
12427185Sminshall 		    sin.sin_port = atoi(*argv);
12527185Sminshall 		    sin.sin_port = htons((u_short)sin.sin_port);
12627185Sminshall 	    }
12727185Sminshall 
12827185Sminshall 	    s = socket(AF_INET, SOCK_STREAM, 0);
12927185Sminshall 	    if (s < 0) {
13027185Sminshall 		    perror("telnetd: socket");;
13127185Sminshall 		    exit(1);
13227185Sminshall 	    }
13327185Sminshall 	    if (bind(s, &sin, sizeof sin) < 0) {
13427185Sminshall 		perror("bind");
13527185Sminshall 		exit(1);
13627185Sminshall 	    }
13727185Sminshall 	    if (listen(s, 1) < 0) {
13827185Sminshall 		perror("listen");
13927185Sminshall 		exit(1);
14027185Sminshall 	    }
14127185Sminshall 	    foo = sizeof sin;
14227185Sminshall 	    ns = accept(s, &sin, &foo);
14327185Sminshall 	    if (ns < 0) {
14427185Sminshall 		perror("accept");
14527185Sminshall 		exit(1);
14627185Sminshall 	    }
14727185Sminshall 	    dup2(ns, 0);
14827185Sminshall 	    close(s);
14927185Sminshall 	}
15027185Sminshall #endif	/* defined(DEBUG) */
15124855Seric 	openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON);
15216371Skarels 	fromlen = sizeof (from);
15316371Skarels 	if (getpeername(0, &from, &fromlen) < 0) {
15416371Skarels 		fprintf(stderr, "%s: ", argv[0]);
15516371Skarels 		perror("getpeername");
15616371Skarels 		_exit(1);
1578346Ssam 	}
15817156Ssam 	if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) {
15917187Sralph 		syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
16010418Ssam 	}
16116371Skarels 	doit(0, &from);
1626002Sroot }
1636002Sroot 
164*27649Sminshall 
165*27649Sminshall /*
166*27649Sminshall  * Get()
167*27649Sminshall  *
168*27649Sminshall  *	Return next character from file descriptor.
169*27649Sminshall  *
170*27649Sminshall  *	This is not meant to be very efficient, since it is only
171*27649Sminshall  * run during startup.
172*27649Sminshall  */
173*27649Sminshall 
174*27649Sminshall Get(f)
175*27649Sminshall int	f;		/* the file descriptor */
176*27649Sminshall {
177*27649Sminshall     char	input;
178*27649Sminshall 
179*27649Sminshall     if (read(f, &input, 1) != 1) {
180*27649Sminshall 	syslog(LOG_ERR, "read: %m\n");
181*27649Sminshall 	exit(1);
182*27649Sminshall     }
183*27649Sminshall     return input&0xff;
184*27649Sminshall }
185*27649Sminshall 
186*27649Sminshall char	*terminaltype;
187*27649Sminshall char	*envinit[2];
1886002Sroot int	cleanup();
1896002Sroot 
1906002Sroot /*
1916002Sroot  * Get a pty, scan input lines.
1926002Sroot  */
19312683Ssam doit(f, who)
19412683Ssam 	int f;
19512683Ssam 	struct sockaddr_in *who;
1966002Sroot {
19720188Skarels 	char *host, *inet_ntoa();
19817583Ssam 	int i, p, t;
1996002Sroot 	struct sgttyb b;
20012683Ssam 	struct hostent *hp;
201*27649Sminshall 	int c;
202*27649Sminshall 	int gotterminaltype = 0;
2036002Sroot 
204*27649Sminshall 	/*
205*27649Sminshall 	 * Try to get a terminal type from the foreign host.
206*27649Sminshall 	 */
207*27649Sminshall 
208*27649Sminshall 	{
209*27649Sminshall 	    static char sbuf[] = { IAC, DO, TELOPT_TTYPE };
210*27649Sminshall 
211*27649Sminshall 	    terminaltype = 0;
212*27649Sminshall 	    if (write(f, sbuf, sizeof sbuf) == -1) {
213*27649Sminshall 		syslog(LOG_ERR, "write sbuf: %m\n");
214*27649Sminshall 		exit(1);
215*27649Sminshall 	    }
216*27649Sminshall 	    for (;;) {		/* ugly, but we are VERY early */
217*27649Sminshall 		while ((c = Get(f)) != IAC) {
218*27649Sminshall 		    NIACCUM(c);
219*27649Sminshall 		}
220*27649Sminshall 		if ((c = Get(f)) == WILL) {
221*27649Sminshall 		    if ((c = Get(f)) == TELOPT_TTYPE) {
222*27649Sminshall 			static char sbbuf[] = { IAC, SB, TELOPT_TTYPE,
223*27649Sminshall 							TELQUAL_SEND, IAC, SE };
224*27649Sminshall 			if (write(f, sbbuf, sizeof sbbuf) == -1) {
225*27649Sminshall 			    syslog(LOG_ERR, "write sbbuf: %m\n");
226*27649Sminshall 			    exit(1);
227*27649Sminshall 			}
228*27649Sminshall 			break;
229*27649Sminshall 		    } else {
230*27649Sminshall 			NIACCUM(IAC);
231*27649Sminshall 			NIACCUM(WILL);
232*27649Sminshall 			NIACCUM(c);
233*27649Sminshall 		    }
234*27649Sminshall 		} else if (c == WONT) {
235*27649Sminshall 		    if ((c = Get(f)) == TELOPT_TTYPE) {
236*27649Sminshall 			terminaltype = "TERM=network";
237*27649Sminshall 			break;
238*27649Sminshall 		    } else {
239*27649Sminshall 			NIACCUM(IAC);
240*27649Sminshall 			NIACCUM(WONT);
241*27649Sminshall 			NIACCUM(c);
242*27649Sminshall 		    }
243*27649Sminshall 		} else {
244*27649Sminshall 		    NIACCUM(IAC);
245*27649Sminshall 		    NIACCUM(c);
246*27649Sminshall 		}
247*27649Sminshall 	    }
248*27649Sminshall 	    if (!terminaltype) {
249*27649Sminshall 		for (;;) {
250*27649Sminshall 		    while ((c = Get(f)) != IAC) {
251*27649Sminshall 			NIACCUM(c);
252*27649Sminshall 		    }
253*27649Sminshall 		    if ((c = Get(f)) != SB) {
254*27649Sminshall 			NIACCUM(IAC);
255*27649Sminshall 			NIACCUM(c);
256*27649Sminshall 		    } else if ((c = Get(f)) != TELOPT_TTYPE) {
257*27649Sminshall 			NIACCUM(IAC);
258*27649Sminshall 			NIACCUM(SB);
259*27649Sminshall 			NIACCUM(c);
260*27649Sminshall 		    } else if ((c = Get(f)) != TELQUAL_IS) {
261*27649Sminshall 			NIACCUM(IAC);
262*27649Sminshall 			NIACCUM(SB);
263*27649Sminshall 			NIACCUM(TELOPT_TTYPE);
264*27649Sminshall 			NIACCUM(c);
265*27649Sminshall 		    } else {		/* Yaaaay! */
266*27649Sminshall 			static char terminalname[5+41] = "TERM=";
267*27649Sminshall 
268*27649Sminshall 			terminaltype = terminalname+strlen(terminalname);
269*27649Sminshall 
270*27649Sminshall 			while (terminaltype <
271*27649Sminshall 				    (terminalname + sizeof terminalname-1)) {
272*27649Sminshall 			    if ((c = Get(f)) == IAC) {
273*27649Sminshall 				if ((c = Get(f)) == SE) {
274*27649Sminshall 				    break;		/* done */
275*27649Sminshall 				} else {
276*27649Sminshall 				    *terminaltype++ = IAC;	/* ? */
277*27649Sminshall 				    if (isupper(c)) {
278*27649Sminshall 					c = tolower(c);
279*27649Sminshall 				    }
280*27649Sminshall 				    *terminaltype++ = c;
281*27649Sminshall 				}
282*27649Sminshall 			    } else {
283*27649Sminshall 				if (isupper(c)) {
284*27649Sminshall 				    c = tolower(c);
285*27649Sminshall 				}
286*27649Sminshall 				*terminaltype++ = c;    /* accumulate name */
287*27649Sminshall 			    }
288*27649Sminshall 			}
289*27649Sminshall 			*terminaltype = 0;
290*27649Sminshall 			terminaltype = terminalname;
291*27649Sminshall 			gotterminaltype = 1;
292*27649Sminshall 			break;
293*27649Sminshall 		    }
294*27649Sminshall 		}
295*27649Sminshall 	    }
296*27649Sminshall 	    envinit[0] = terminaltype;
297*27649Sminshall 	    envinit[1] = 0;
298*27649Sminshall 	}
299*27649Sminshall 
30020188Skarels 	for (c = 'p'; c <= 's'; c++) {
30120188Skarels 		struct stat stb;
30220188Skarels 
30320188Skarels 		line = "/dev/ptyXX";
30420188Skarels 		line[strlen("/dev/pty")] = c;
30520188Skarels 		line[strlen("/dev/ptyp")] = '0';
30620188Skarels 		if (stat(line, &stb) < 0)
30720188Skarels 			break;
30817583Ssam 		for (i = 0; i < 16; i++) {
30920188Skarels 			line[strlen("/dev/ptyp")] = "0123456789abcdef"[i];
31020188Skarels 			p = open(line, 2);
31117583Ssam 			if (p > 0)
31217583Ssam 				goto gotpty;
31317583Ssam 		}
3146002Sroot 	}
3159244Ssam 	fatal(f, "All network ports in use");
3169244Ssam 	/*NOTREACHED*/
3176002Sroot gotpty:
3186002Sroot 	dup2(f, 0);
31920188Skarels 	line[strlen("/dev/")] = 't';
32017583Ssam 	t = open("/dev/tty", O_RDWR);
3216002Sroot 	if (t >= 0) {
3226002Sroot 		ioctl(t, TIOCNOTTY, 0);
3236002Sroot 		close(t);
3246002Sroot 	}
32520188Skarels 	t = open(line, O_RDWR);
3269244Ssam 	if (t < 0)
32720188Skarels 		fatalperror(f, line, errno);
3286002Sroot 	ioctl(t, TIOCGETP, &b);
3296388Ssam 	b.sg_flags = CRMOD|XTABS|ANYP;
3306002Sroot 	ioctl(t, TIOCSETP, &b);
3316388Ssam 	ioctl(p, TIOCGETP, &b);
3328379Ssam 	b.sg_flags &= ~ECHO;
3336388Ssam 	ioctl(p, TIOCSETP, &b);
33412683Ssam 	hp = gethostbyaddr(&who->sin_addr, sizeof (struct in_addr),
33512683Ssam 		who->sin_family);
33612683Ssam 	if (hp)
33712683Ssam 		host = hp->h_name;
33812683Ssam 	else
33917444Sralph 		host = inet_ntoa(who->sin_addr);
3409244Ssam 	if ((i = fork()) < 0)
3419244Ssam 		fatalperror(f, "fork", errno);
3426002Sroot 	if (i)
3436002Sroot 		telnet(f, p);
3446002Sroot 	close(f);
3456002Sroot 	close(p);
3466002Sroot 	dup2(t, 0);
3476002Sroot 	dup2(t, 1);
3486002Sroot 	dup2(t, 2);
3496002Sroot 	close(t);
35013799Ssam 	environ = envinit;
351*27649Sminshall 	/*
352*27649Sminshall 	 * -h : pass on name of host.
353*27649Sminshall 	 * -p : don't clobber the environment (so terminal type stays set).
354*27649Sminshall 	 */
355*27649Sminshall 	execl("/bin/login", "login", "-h", host,
356*27649Sminshall 					gotterminaltype ? "-p" : 0, 0);
3579244Ssam 	fatalperror(f, "/bin/login", errno);
3589244Ssam 	/*NOTREACHED*/
3599244Ssam }
3609244Ssam 
3619244Ssam fatal(f, msg)
3629244Ssam 	int f;
3639244Ssam 	char *msg;
3649244Ssam {
3659244Ssam 	char buf[BUFSIZ];
3669244Ssam 
36717583Ssam 	(void) sprintf(buf, "telnetd: %s.\r\n", msg);
3689244Ssam 	(void) write(f, buf, strlen(buf));
3696002Sroot 	exit(1);
3706002Sroot }
3716002Sroot 
3729244Ssam fatalperror(f, msg, errno)
3739244Ssam 	int f;
3749244Ssam 	char *msg;
3759244Ssam 	int errno;
3769244Ssam {
3779244Ssam 	char buf[BUFSIZ];
3789244Ssam 	extern char *sys_errlist[];
3799244Ssam 
38017583Ssam 	(void) sprintf(buf, "%s: %s\r\n", msg, sys_errlist[errno]);
3819244Ssam 	fatal(f, buf);
3829244Ssam }
3839244Ssam 
38427185Sminshall 
3856002Sroot /*
38627185Sminshall  * Check a descriptor to see if out of band data exists on it.
38727185Sminshall  */
38827185Sminshall 
38927185Sminshall 
39027185Sminshall stilloob(s)
39127185Sminshall int	s;		/* socket number */
39227185Sminshall {
39327185Sminshall     static struct timeval timeout = { 0 };
39427185Sminshall     fd_set	excepts;
39527185Sminshall     int value;
39627185Sminshall 
39727185Sminshall     do {
39827185Sminshall 	FD_ZERO(&excepts);
39927185Sminshall 	FD_SET(s, &excepts);
40027185Sminshall 	value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout);
40127185Sminshall     } while ((value == -1) && (errno = EINTR));
40227185Sminshall 
40327185Sminshall     if (value < 0) {
40427185Sminshall 	fatalperror(pty, "select", errno);
40527185Sminshall     }
40627185Sminshall     if (FD_ISSET(s, &excepts)) {
40727185Sminshall 	return 1;
40827185Sminshall     } else {
40927185Sminshall 	return 0;
41027185Sminshall     }
41127185Sminshall }
41227185Sminshall 
41327185Sminshall /*
4146002Sroot  * Main loop.  Select from pty and network, and
4156002Sroot  * hand data to telnet receiver finite state machine.
4166002Sroot  */
4176002Sroot telnet(f, p)
4186002Sroot {
4196002Sroot 	int on = 1;
42012713Ssam 	char hostname[32];
4216002Sroot 
4226002Sroot 	net = f, pty = p;
4236002Sroot 	ioctl(f, FIONBIO, &on);
4246002Sroot 	ioctl(p, FIONBIO, &on);
425*27649Sminshall #if	defined(SO_OOBINLINE)
426*27649Sminshall 	setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on);
427*27649Sminshall #endif	/* defined(SO_OOBINLINE) */
4286002Sroot 	signal(SIGTSTP, SIG_IGN);
42913028Ssam 	signal(SIGCHLD, cleanup);
43026083Slepreau 	setpgrp(0, 0);
4316002Sroot 
4328379Ssam 	/*
43327185Sminshall 	 * Request to do remote echo and to suppress go ahead.
4348379Ssam 	 */
4358379Ssam 	dooption(TELOPT_ECHO);
43627185Sminshall 	dooption(TELOPT_SGA);
43712713Ssam 	/*
438*27649Sminshall 	 * Is the client side a 4.2 (NOT 4.3) system?  We need to know this
439*27649Sminshall 	 * because 4.2 clients are unable to deal with TCP urgent data.
440*27649Sminshall 	 *
441*27649Sminshall 	 * To find out, we send out a "DO ECHO".  If the remote system
442*27649Sminshall 	 * answers "WILL ECHO" it is probably a 4.2 client, and we note
443*27649Sminshall 	 * that fact ("WILL ECHO" ==> that the client will echo what
444*27649Sminshall 	 * WE, the server, sends it; it does NOT mean that the client will
445*27649Sminshall 	 * echo the terminal input).
446*27649Sminshall 	 */
447*27649Sminshall 	sprintf(nfrontp, doopt, TELOPT_ECHO);
448*27649Sminshall 	nfrontp += sizeof doopt-2;
449*27649Sminshall 	hisopts[TELOPT_ECHO] = OPT_ALWAYS_LOOK;
450*27649Sminshall 
451*27649Sminshall 	/*
45212713Ssam 	 * Show banner that getty never gave.
45312713Ssam 	 */
45412713Ssam 	gethostname(hostname, sizeof (hostname));
45512713Ssam 	sprintf(nfrontp, BANNER, hostname, "");
45612713Ssam 	nfrontp += strlen(nfrontp);
457*27649Sminshall 
458*27649Sminshall 	/*
459*27649Sminshall 	 * Call telrcv() once to pick up anything received during
460*27649Sminshall 	 * terminal type negotiation.
461*27649Sminshall 	 */
462*27649Sminshall 	telrcv();
463*27649Sminshall 
4646002Sroot 	for (;;) {
46527185Sminshall 		fd_set ibits, obits, xbits;
4666002Sroot 		register int c;
4676002Sroot 
46827185Sminshall 		if (ncc < 0 && pcc < 0)
46927185Sminshall 			break;
47027185Sminshall 
47127185Sminshall 		FD_ZERO(&ibits);
47227185Sminshall 		FD_ZERO(&obits);
47327185Sminshall 		FD_ZERO(&xbits);
4746002Sroot 		/*
4756002Sroot 		 * Never look for input if there's still
4766002Sroot 		 * stuff in the corresponding output buffer
4776002Sroot 		 */
47827185Sminshall 		if (nfrontp - nbackp || pcc > 0) {
47927185Sminshall 			FD_SET(f, &obits);
48027185Sminshall 		} else {
48127185Sminshall 			FD_SET(p, &ibits);
48227185Sminshall 		}
48327185Sminshall 		if (pfrontp - pbackp || ncc > 0) {
48427185Sminshall 			FD_SET(p, &obits);
48527185Sminshall 		} else {
48627185Sminshall 			FD_SET(f, &ibits);
48727185Sminshall 		}
48827185Sminshall 		if (!SYNCHing) {
48927185Sminshall 			FD_SET(f, &xbits);
49027185Sminshall 		}
49127185Sminshall 		if ((c = select(16, &ibits, &obits, &xbits,
49227185Sminshall 						(struct timeval *)0)) < 1) {
49327185Sminshall 			if (c == -1) {
49427185Sminshall 				if (errno == EINTR) {
49527185Sminshall 					continue;
49627185Sminshall 				}
49727185Sminshall 			}
4986002Sroot 			sleep(5);
4996002Sroot 			continue;
5006002Sroot 		}
5016002Sroot 
5026002Sroot 		/*
50327185Sminshall 		 * Any urgent data?
50427185Sminshall 		 */
50527185Sminshall 		if (FD_ISSET(net, &xbits)) {
50627185Sminshall 		    SYNCHing = 1;
50727185Sminshall 		}
50827185Sminshall 
50927185Sminshall 		/*
5106002Sroot 		 * Something to read from the network...
5116002Sroot 		 */
51227185Sminshall 		if (FD_ISSET(net, &ibits)) {
513*27649Sminshall #if	!defined(SO_OOBINLINE)
51427185Sminshall 			/*
51527185Sminshall 			 * In 4.2 (and some early 4.3) systems, the
51627185Sminshall 			 * OOB indication and data handling in the kernel
51727185Sminshall 			 * is such that if two separate TCP Urgent requests
51827185Sminshall 			 * come in, one byte of TCP data will be overlaid.
51927185Sminshall 			 * This is fatal for Telnet, but we try to live
52027185Sminshall 			 * with it.
52127185Sminshall 			 *
52227185Sminshall 			 * In addition, in 4.2 (and...), a special protocol
52327185Sminshall 			 * is needed to pick up the TCP Urgent data in
52427185Sminshall 			 * the correct sequence.
52527185Sminshall 			 *
52627185Sminshall 			 * What we do is:  if we think we are in urgent
52727185Sminshall 			 * mode, we look to see if we are "at the mark".
52827185Sminshall 			 * If we are, we do an OOB receive.  If we run
52927185Sminshall 			 * this twice, we will do the OOB receive twice,
53027185Sminshall 			 * but the second will fail, since the second
53127185Sminshall 			 * time we were "at the mark", but there wasn't
53227185Sminshall 			 * any data there (the kernel doesn't reset
53327185Sminshall 			 * "at the mark" until we do a normal read).
53427185Sminshall 			 * Once we've read the OOB data, we go ahead
53527185Sminshall 			 * and do normal reads.
53627185Sminshall 			 *
53727185Sminshall 			 * There is also another problem, which is that
53827185Sminshall 			 * since the OOB byte we read doesn't put us
53927185Sminshall 			 * out of OOB state, and since that byte is most
54027185Sminshall 			 * likely the TELNET DM (data mark), we would
54127185Sminshall 			 * stay in the TELNET SYNCH (SYNCHing) state.
54227185Sminshall 			 * So, clocks to the rescue.  If we've "just"
54327185Sminshall 			 * received a DM, then we test for the
54427185Sminshall 			 * presence of OOB data when the receive OOB
54527185Sminshall 			 * fails (and AFTER we did the normal mode read
54627185Sminshall 			 * to clear "at the mark").
54727185Sminshall 			 */
54827185Sminshall 		    if (SYNCHing) {
54927185Sminshall 			int atmark;
55027185Sminshall 
55127185Sminshall 			ioctl(net, SIOCATMARK, (char *)&atmark);
55227185Sminshall 			if (atmark) {
55327185Sminshall 			    ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB);
55427185Sminshall 			    if ((ncc == -1) && (errno == EINVAL)) {
55527185Sminshall 				ncc = read(net, netibuf, sizeof (netibuf));
55627185Sminshall 				if (clocks.didnetreceive < clocks.gotDM) {
55727185Sminshall 				    SYNCHing = stilloob(net);
55827185Sminshall 				}
55927185Sminshall 			    }
56027185Sminshall 			} else {
56127185Sminshall 			    ncc = read(net, netibuf, sizeof (netibuf));
5626002Sroot 			}
56327185Sminshall 		    } else {
56427185Sminshall 			ncc = read(net, netibuf, sizeof (netibuf));
56527185Sminshall 		    }
56627185Sminshall 		    settimer(didnetreceive);
567*27649Sminshall #else	/* !defined(SO_OOBINLINE)) */
56827185Sminshall 		    ncc = read(net, netibuf, sizeof (netibuf));
569*27649Sminshall #endif	/* !defined(SO_OOBINLINE)) */
57027185Sminshall 		    if (ncc < 0 && errno == EWOULDBLOCK)
57127185Sminshall 			ncc = 0;
57227185Sminshall 		    else {
57327185Sminshall 			if (ncc <= 0) {
57427185Sminshall 			    break;
57527185Sminshall 			}
57627185Sminshall 			netip = netibuf;
57727185Sminshall 		    }
5786002Sroot 		}
5796002Sroot 
5806002Sroot 		/*
5816002Sroot 		 * Something to read from the pty...
5826002Sroot 		 */
58327185Sminshall 		if (FD_ISSET(p, &ibits)) {
5846002Sroot 			pcc = read(p, ptyibuf, BUFSIZ);
5856002Sroot 			if (pcc < 0 && errno == EWOULDBLOCK)
5866002Sroot 				pcc = 0;
5876002Sroot 			else {
5886002Sroot 				if (pcc <= 0)
5896002Sroot 					break;
5906002Sroot 				ptyip = ptyibuf;
5916002Sroot 			}
5926002Sroot 		}
5936002Sroot 
5946002Sroot 		while (pcc > 0) {
5956002Sroot 			if ((&netobuf[BUFSIZ] - nfrontp) < 2)
5966002Sroot 				break;
5976002Sroot 			c = *ptyip++ & 0377, pcc--;
5986002Sroot 			if (c == IAC)
5996002Sroot 				*nfrontp++ = c;
6006002Sroot 			*nfrontp++ = c;
60127020Sminshall 			if (c == '\r') {
60227020Sminshall 				if (pcc > 0 && ((*ptyip & 0377) == '\n')) {
60327020Sminshall 					*nfrontp++ = *ptyip++ & 0377;
60427020Sminshall 					pcc--;
60527020Sminshall 				} else
60627020Sminshall 					*nfrontp++ = '\0';
60727020Sminshall 			}
6086002Sroot 		}
60927185Sminshall 		if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0)
6106002Sroot 			netflush();
6116002Sroot 		if (ncc > 0)
6126002Sroot 			telrcv();
61327185Sminshall 		if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0)
6146002Sroot 			ptyflush();
6156002Sroot 	}
6166002Sroot 	cleanup();
6176002Sroot }
6186002Sroot 
6196002Sroot /*
6206002Sroot  * State for recv fsm
6216002Sroot  */
6226002Sroot #define	TS_DATA		0	/* base state */
6236002Sroot #define	TS_IAC		1	/* look for double IAC's */
6246002Sroot #define	TS_CR		2	/* CR-LF ->'s CR */
625*27649Sminshall #define	TS_SB		3	/* throw away begin's... */
626*27649Sminshall #define	TS_SE		4	/* ...end's (suboption negotiation) */
6276002Sroot #define	TS_WILL		5	/* will option negotiation */
6286002Sroot #define	TS_WONT		6	/* wont " */
6296002Sroot #define	TS_DO		7	/* do " */
6306002Sroot #define	TS_DONT		8	/* dont " */
6316002Sroot 
6326002Sroot telrcv()
6336002Sroot {
6346002Sroot 	register int c;
6356002Sroot 	static int state = TS_DATA;
6366002Sroot 
6376002Sroot 	while (ncc > 0) {
6386002Sroot 		if ((&ptyobuf[BUFSIZ] - pfrontp) < 2)
6396002Sroot 			return;
6406002Sroot 		c = *netip++ & 0377, ncc--;
6416002Sroot 		switch (state) {
6426002Sroot 
64326090Sminshall 		case TS_CR:
64426090Sminshall 			state = TS_DATA;
64526499Sminshall 			if ((c == 0) || (c == '\n')) {
64626090Sminshall 				break;
64726499Sminshall 			}
64826090Sminshall 			/* FALL THROUGH */
64926090Sminshall 
6506002Sroot 		case TS_DATA:
6516002Sroot 			if (c == IAC) {
6526002Sroot 				state = TS_IAC;
6536002Sroot 				break;
6546002Sroot 			}
6556002Sroot 			if (inter > 0)
6566002Sroot 				break;
65727020Sminshall 			/*
65827020Sminshall 			 * We map \r\n ==> \n, since \r\n says
65927020Sminshall 			 * that we want to be in column 1 of the next
66027020Sminshall 			 * printable line, and \n is the standard
66127020Sminshall 			 * unix way of saying that (\r is only good
66227020Sminshall 			 * if CRMOD is set, which it normally is).
66327020Sminshall 			 */
664*27649Sminshall 			if ((myopts[TELOPT_BINARY] == OPT_DONT) && c == '\r') {
66527020Sminshall 				if ((ncc > 0) && ('\n' == *netip)) {
66627020Sminshall 					netip++; ncc--;
66727020Sminshall 					c = '\n';
66827020Sminshall 				} else {
66927020Sminshall 					state = TS_CR;
67027020Sminshall 				}
67126499Sminshall 			}
67226499Sminshall 			*pfrontp++ = c;
6736002Sroot 			break;
6746002Sroot 
6756002Sroot 		case TS_IAC:
6766002Sroot 			switch (c) {
6776002Sroot 
6786002Sroot 			/*
6796002Sroot 			 * Send the process on the pty side an
6806002Sroot 			 * interrupt.  Do this with a NULL or
6816002Sroot 			 * interrupt char; depending on the tty mode.
6826002Sroot 			 */
6836002Sroot 			case IP:
6846002Sroot 				interrupt();
6856002Sroot 				break;
6866002Sroot 
68727229Sminshall 			case BREAK:
68827229Sminshall 				sendbrk();
68927229Sminshall 				break;
69027229Sminshall 
6916002Sroot 			/*
6926002Sroot 			 * Are You There?
6936002Sroot 			 */
6946002Sroot 			case AYT:
69517583Ssam 				strcpy(nfrontp, "\r\n[Yes]\r\n");
69617583Ssam 				nfrontp += 9;
6976002Sroot 				break;
6986002Sroot 
6996002Sroot 			/*
70027185Sminshall 			 * Abort Output
70127185Sminshall 			 */
70227185Sminshall 			case AO: {
70327185Sminshall 					struct ltchars tmpltc;
70427185Sminshall 
70527185Sminshall 					ptyflush();	/* half-hearted */
70627185Sminshall 					ioctl(pty, TIOCGLTC, &tmpltc);
70727185Sminshall 					if (tmpltc.t_flushc != '\377') {
70827185Sminshall 						*pfrontp++ = tmpltc.t_flushc;
70927185Sminshall 					}
71027260Sminshall 					netclear();	/* clear buffer back */
71127185Sminshall 					*nfrontp++ = IAC;
71227185Sminshall 					*nfrontp++ = DM;
71327187Sminshall 					neturg = nfrontp-1; /* off by one XXX */
71427185Sminshall 					break;
71527185Sminshall 				}
71627185Sminshall 
71727185Sminshall 			/*
7186002Sroot 			 * Erase Character and
7196002Sroot 			 * Erase Line
7206002Sroot 			 */
7216002Sroot 			case EC:
72227185Sminshall 			case EL: {
72327185Sminshall 					struct sgttyb b;
72427185Sminshall 					char ch;
7256002Sroot 
72627185Sminshall 					ptyflush();	/* half-hearted */
72727185Sminshall 					ioctl(pty, TIOCGETP, &b);
72827185Sminshall 					ch = (c == EC) ?
72927185Sminshall 						b.sg_erase : b.sg_kill;
73027185Sminshall 					if (ch != '\377') {
73127185Sminshall 						*pfrontp++ = ch;
73227185Sminshall 					}
73327185Sminshall 					break;
73427185Sminshall 				}
73527185Sminshall 
7366002Sroot 			/*
7376002Sroot 			 * Check for urgent data...
7386002Sroot 			 */
7396002Sroot 			case DM:
74027185Sminshall 				SYNCHing = stilloob(net);
74127185Sminshall 				settimer(gotDM);
7426002Sroot 				break;
7436002Sroot 
74427185Sminshall 
7456002Sroot 			/*
7466002Sroot 			 * Begin option subnegotiation...
7476002Sroot 			 */
7486002Sroot 			case SB:
749*27649Sminshall 				state = TS_SB;
7506002Sroot 				continue;
7516002Sroot 
7526002Sroot 			case WILL:
75327188Sminshall 				state = TS_WILL;
75427188Sminshall 				continue;
75527188Sminshall 
7566002Sroot 			case WONT:
75727188Sminshall 				state = TS_WONT;
75827188Sminshall 				continue;
75927188Sminshall 
7606002Sroot 			case DO:
76127188Sminshall 				state = TS_DO;
76227188Sminshall 				continue;
76327188Sminshall 
7646002Sroot 			case DONT:
76527188Sminshall 				state = TS_DONT;
7666002Sroot 				continue;
7676002Sroot 
7686002Sroot 			case IAC:
7696002Sroot 				*pfrontp++ = c;
7706002Sroot 				break;
7716002Sroot 			}
7726002Sroot 			state = TS_DATA;
7736002Sroot 			break;
7746002Sroot 
775*27649Sminshall 		case TS_SB:
776*27649Sminshall 			if (c == IAC) {
777*27649Sminshall 				state = TS_SE;
778*27649Sminshall 			} else {
779*27649Sminshall 				SB_ACCUM(c);
780*27649Sminshall 			}
7816002Sroot 			break;
7826002Sroot 
783*27649Sminshall 		case TS_SE:
784*27649Sminshall 			if (c != SE) {
785*27649Sminshall 				if (c != IAC) {
786*27649Sminshall 					SB_ACCUM(IAC);
787*27649Sminshall 				}
788*27649Sminshall 				SB_ACCUM(c);
789*27649Sminshall 				state = TS_SB;
790*27649Sminshall 			} else {
791*27649Sminshall 				SB_TERM();
792*27649Sminshall 				suboption();	/* handle sub-option */
793*27649Sminshall 				state = TS_DATA;
794*27649Sminshall 			}
7956002Sroot 			break;
7966002Sroot 
7976002Sroot 		case TS_WILL:
798*27649Sminshall 			if (hisopts[c] != OPT_WILL)
7996002Sroot 				willoption(c);
8006002Sroot 			state = TS_DATA;
8016002Sroot 			continue;
8026002Sroot 
8036002Sroot 		case TS_WONT:
804*27649Sminshall 			if (hisopts[c] != OPT_WONT)
8056002Sroot 				wontoption(c);
8066002Sroot 			state = TS_DATA;
8076002Sroot 			continue;
8086002Sroot 
8096002Sroot 		case TS_DO:
810*27649Sminshall 			if (myopts[c] != OPT_DO)
8116002Sroot 				dooption(c);
8126002Sroot 			state = TS_DATA;
8136002Sroot 			continue;
8146002Sroot 
8156002Sroot 		case TS_DONT:
816*27649Sminshall 			if (myopts[c] != OPT_DONT) {
817*27649Sminshall 				dontoption(c);
8186002Sroot 			}
8196002Sroot 			state = TS_DATA;
8206002Sroot 			continue;
8216002Sroot 
8226002Sroot 		default:
8239218Ssam 			printf("telnetd: panic state=%d\n", state);
8246002Sroot 			exit(1);
8256002Sroot 		}
8266002Sroot 	}
8276002Sroot }
8286002Sroot 
8296002Sroot willoption(option)
8306002Sroot 	int option;
8316002Sroot {
8326002Sroot 	char *fmt;
8336002Sroot 
8346002Sroot 	switch (option) {
8356002Sroot 
8366002Sroot 	case TELOPT_BINARY:
8376002Sroot 		mode(RAW, 0);
83827188Sminshall 		fmt = doopt;
83927188Sminshall 		break;
8406002Sroot 
8416002Sroot 	case TELOPT_ECHO:
842*27649Sminshall 		not42 = 0;		/* looks like a 4.2 system */
843*27649Sminshall 		/*
844*27649Sminshall 		 * Now, in a 4.2 system, to break them out of ECHOing
845*27649Sminshall 		 * (to the terminal) mode, we need to send a "WILL ECHO".
846*27649Sminshall 		 * Kludge upon kludge!
847*27649Sminshall 		 */
848*27649Sminshall 		if (myopts[TELOPT_ECHO] == OPT_DO) {
849*27649Sminshall 		    dooption(TELOPT_ECHO);
850*27649Sminshall 		}
851*27649Sminshall 		fmt = dont;
85227188Sminshall 		break;
8536002Sroot 
854*27649Sminshall 	case TELOPT_TTYPE:
8556002Sroot 	case TELOPT_SGA:
8566002Sroot 		fmt = doopt;
8576002Sroot 		break;
8586002Sroot 
8596002Sroot 	case TELOPT_TM:
8606002Sroot 		fmt = dont;
8616002Sroot 		break;
8626002Sroot 
8636002Sroot 	default:
8646002Sroot 		fmt = dont;
8656002Sroot 		break;
8666002Sroot 	}
86727188Sminshall 	if (fmt == doopt) {
868*27649Sminshall 		hisopts[option] = OPT_WILL;
86927188Sminshall 	} else {
870*27649Sminshall 		hisopts[option] = OPT_WONT;
87127188Sminshall 	}
8726023Ssam 	sprintf(nfrontp, fmt, option);
8738379Ssam 	nfrontp += sizeof (dont) - 2;
8746002Sroot }
8756002Sroot 
8766002Sroot wontoption(option)
8776002Sroot 	int option;
8786002Sroot {
8796002Sroot 	char *fmt;
8806002Sroot 
8816002Sroot 	switch (option) {
8826002Sroot 	case TELOPT_ECHO:
883*27649Sminshall 		not42 = 1;		/* doesn't seem to be a 4.2 system */
88427188Sminshall 		break;
8856002Sroot 
8866002Sroot 	case TELOPT_BINARY:
8876002Sroot 		mode(0, RAW);
8886002Sroot 		break;
8896002Sroot 	}
89027188Sminshall 	fmt = dont;
891*27649Sminshall 	hisopts[option] = OPT_WONT;
8926002Sroot 	sprintf(nfrontp, fmt, option);
8938379Ssam 	nfrontp += sizeof (doopt) - 2;
8946002Sroot }
8956002Sroot 
8966002Sroot dooption(option)
8976002Sroot 	int option;
8986002Sroot {
8996002Sroot 	char *fmt;
9006002Sroot 
9016002Sroot 	switch (option) {
9026002Sroot 
9036002Sroot 	case TELOPT_TM:
9046002Sroot 		fmt = wont;
9056002Sroot 		break;
9066002Sroot 
9076002Sroot 	case TELOPT_ECHO:
9086002Sroot 		mode(ECHO|CRMOD, 0);
90927188Sminshall 		fmt = will;
91027188Sminshall 		break;
9116002Sroot 
9126002Sroot 	case TELOPT_BINARY:
9136002Sroot 		mode(RAW, 0);
91427188Sminshall 		fmt = will;
91527188Sminshall 		break;
9166002Sroot 
9176002Sroot 	case TELOPT_SGA:
9186002Sroot 		fmt = will;
9196002Sroot 		break;
9206002Sroot 
9216002Sroot 	default:
9226002Sroot 		fmt = wont;
9236002Sroot 		break;
9246002Sroot 	}
92527188Sminshall 	if (fmt == will) {
926*27649Sminshall 	    myopts[option] = OPT_DO;
92727188Sminshall 	} else {
928*27649Sminshall 	    myopts[option] = OPT_DONT;
92927188Sminshall 	}
9306002Sroot 	sprintf(nfrontp, fmt, option);
9318379Ssam 	nfrontp += sizeof (doopt) - 2;
9326002Sroot }
9336002Sroot 
934*27649Sminshall 
935*27649Sminshall dontoption(option)
936*27649Sminshall int option;
937*27649Sminshall {
938*27649Sminshall     char *fmt;
939*27649Sminshall 
940*27649Sminshall     switch (option) {
941*27649Sminshall     case TELOPT_ECHO:		/* we should stop echoing */
942*27649Sminshall 	mode(0, ECHO|CRMOD);
943*27649Sminshall 	fmt = wont;
944*27649Sminshall 	break;
945*27649Sminshall     default:
946*27649Sminshall 	fmt = wont;
947*27649Sminshall 	break;
948*27649Sminshall     }
949*27649Sminshall     if (fmt = wont) {
950*27649Sminshall 	myopts[option] = OPT_DONT;
951*27649Sminshall     } else {
952*27649Sminshall 	myopts[option] = OPT_DO;
953*27649Sminshall     }
954*27649Sminshall     sprintf(nfrontp, fmt, option);
955*27649Sminshall     nfrontp += sizeof (wont) - 2;
956*27649Sminshall }
957*27649Sminshall 
958*27649Sminshall /*
959*27649Sminshall  * suboption()
960*27649Sminshall  *
961*27649Sminshall  *	Look at the sub-option buffer, and try to be helpful to the other
962*27649Sminshall  * side.
963*27649Sminshall  *
964*27649Sminshall  *	Currently we recognize:
965*27649Sminshall  *
966*27649Sminshall  *	(nothing - we only do terminal type at start-up time)
967*27649Sminshall  */
968*27649Sminshall 
969*27649Sminshall suboption()
970*27649Sminshall {
971*27649Sminshall     switch (subbuffer[0]&0xff) {
972*27649Sminshall     default:
973*27649Sminshall 	;
974*27649Sminshall     }
975*27649Sminshall }
976*27649Sminshall 
9776002Sroot mode(on, off)
9786002Sroot 	int on, off;
9796002Sroot {
9806002Sroot 	struct sgttyb b;
9816002Sroot 
9826002Sroot 	ptyflush();
9836002Sroot 	ioctl(pty, TIOCGETP, &b);
9846002Sroot 	b.sg_flags |= on;
9856002Sroot 	b.sg_flags &= ~off;
9866002Sroot 	ioctl(pty, TIOCSETP, &b);
9876002Sroot }
9886002Sroot 
9896002Sroot /*
9906002Sroot  * Send interrupt to process on other side of pty.
9916002Sroot  * If it is in raw mode, just write NULL;
9926002Sroot  * otherwise, write intr char.
9936002Sroot  */
9946002Sroot interrupt()
9956002Sroot {
9966002Sroot 	struct sgttyb b;
9976002Sroot 	struct tchars tchars;
9986002Sroot 
9996002Sroot 	ptyflush();	/* half-hearted */
10006002Sroot 	ioctl(pty, TIOCGETP, &b);
10016002Sroot 	if (b.sg_flags & RAW) {
10026002Sroot 		*pfrontp++ = '\0';
10036002Sroot 		return;
10046002Sroot 	}
10056002Sroot 	*pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ?
10066002Sroot 		'\177' : tchars.t_intrc;
10076002Sroot }
10086002Sroot 
100927229Sminshall /*
101027229Sminshall  * Send quit to process on other side of pty.
101127229Sminshall  * If it is in raw mode, just write NULL;
101227229Sminshall  * otherwise, write quit char.
101327229Sminshall  */
101427229Sminshall sendbrk()
101527229Sminshall {
101627229Sminshall 	struct sgttyb b;
101727229Sminshall 	struct tchars tchars;
101827229Sminshall 
101927229Sminshall 	ptyflush();	/* half-hearted */
102027229Sminshall 	ioctl(pty, TIOCGETP, &b);
102127229Sminshall 	if (b.sg_flags & RAW) {
102227229Sminshall 		*pfrontp++ = '\0';
102327229Sminshall 		return;
102427229Sminshall 	}
102527229Sminshall 	*pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ?
102627229Sminshall 		'\034' : tchars.t_quitc;
102727229Sminshall }
102827229Sminshall 
10296002Sroot ptyflush()
10306002Sroot {
10316002Sroot 	int n;
10326002Sroot 
10336002Sroot 	if ((n = pfrontp - pbackp) > 0)
10346002Sroot 		n = write(pty, pbackp, n);
10358346Ssam 	if (n < 0)
10368346Ssam 		return;
10376002Sroot 	pbackp += n;
10386002Sroot 	if (pbackp == pfrontp)
10396002Sroot 		pbackp = pfrontp = ptyobuf;
10406002Sroot }
104127260Sminshall 
104227260Sminshall /*
104327260Sminshall  * nextitem()
104427260Sminshall  *
104527260Sminshall  *	Return the address of the next "item" in the TELNET data
104627260Sminshall  * stream.  This will be the address of the next character if
104727260Sminshall  * the current address is a user data character, or it will
104827260Sminshall  * be the address of the character following the TELNET command
104927260Sminshall  * if the current address is a TELNET IAC ("I Am a Command")
105027260Sminshall  * character.
105127260Sminshall  */
10526002Sroot 
105327260Sminshall char *
105427260Sminshall nextitem(current)
105527260Sminshall char	*current;
10566002Sroot {
105727260Sminshall     if ((*current&0xff) != IAC) {
105827260Sminshall 	return current+1;
105927260Sminshall     }
106027260Sminshall     switch (*(current+1)&0xff) {
106127260Sminshall     case DO:
106227260Sminshall     case DONT:
106327260Sminshall     case WILL:
106427260Sminshall     case WONT:
106527260Sminshall 	return current+3;
106627260Sminshall     case SB:		/* loop forever looking for the SE */
106727260Sminshall 	{
106827260Sminshall 	    register char *look = current+2;
10696002Sroot 
107027260Sminshall 	    for (;;) {
107127260Sminshall 		if ((*look++&0xff) == IAC) {
107227260Sminshall 		    if ((*look++&0xff) == SE) {
107327260Sminshall 			return look;
107427260Sminshall 		    }
107527260Sminshall 		}
107627260Sminshall 	    }
10778346Ssam 	}
107827260Sminshall     default:
107927260Sminshall 	return current+2;
108027260Sminshall     }
10816002Sroot }
10826002Sroot 
108327185Sminshall 
108427185Sminshall /*
108527260Sminshall  * netclear()
108627260Sminshall  *
108727260Sminshall  *	We are about to do a TELNET SYNCH operation.  Clear
108827260Sminshall  * the path to the network.
108927260Sminshall  *
109027260Sminshall  *	Things are a bit tricky since we may have sent the first
109127260Sminshall  * byte or so of a previous TELNET command into the network.
109227260Sminshall  * So, we have to scan the network buffer from the beginning
109327260Sminshall  * until we are up to where we want to be.
109427260Sminshall  *
109527260Sminshall  *	A side effect of what we do, just to keep things
109627260Sminshall  * simple, is to clear the urgent data pointer.  The principal
109727260Sminshall  * caller should be setting the urgent data pointer AFTER calling
109827260Sminshall  * us in any case.
109927260Sminshall  */
110027260Sminshall 
110127260Sminshall netclear()
110227260Sminshall {
110327260Sminshall     register char *thisitem, *next;
110427260Sminshall     char *good;
110527260Sminshall #define	wewant(p)	((nfrontp > p) && ((*p&0xff) == IAC) && \
110627260Sminshall 				((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
110727260Sminshall 
110827260Sminshall     thisitem = netobuf;
110927260Sminshall 
111027260Sminshall     while ((next = nextitem(thisitem)) <= nbackp) {
111127260Sminshall 	thisitem = next;
111227260Sminshall     }
111327260Sminshall 
111427260Sminshall     /* Now, thisitem is first before/at boundary. */
111527260Sminshall 
111627260Sminshall     good = netobuf;	/* where the good bytes go */
111727260Sminshall 
111827260Sminshall     while (nfrontp > thisitem) {
111927260Sminshall 	if (wewant(thisitem)) {
112027260Sminshall 	    int length;
112127260Sminshall 
112227260Sminshall 	    next = thisitem;
112327260Sminshall 	    do {
112427260Sminshall 		next = nextitem(next);
112527260Sminshall 	    } while (wewant(next) && (nfrontp > next));
112627260Sminshall 	    length = next-thisitem;
112727260Sminshall 	    bcopy(thisitem, good, length);
112827260Sminshall 	    good += length;
112927260Sminshall 	    thisitem = next;
113027260Sminshall 	} else {
113127260Sminshall 	    thisitem = nextitem(thisitem);
113227260Sminshall 	}
113327260Sminshall     }
113427260Sminshall 
113527260Sminshall     nbackp = netobuf;
113627260Sminshall     nfrontp = good;		/* next byte to be sent */
113727260Sminshall     neturg = 0;
113827260Sminshall }
113927260Sminshall 
114027260Sminshall /*
114127185Sminshall  *  netflush
114227185Sminshall  *		Send as much data as possible to the network,
114327185Sminshall  *	handling requests for urgent data.
114427185Sminshall  */
114527185Sminshall 
114627185Sminshall 
114727185Sminshall netflush()
114827185Sminshall {
114927185Sminshall     int n;
115027185Sminshall 
115127185Sminshall     if ((n = nfrontp - nbackp) > 0) {
1152*27649Sminshall 	/*
1153*27649Sminshall 	 * if no urgent data, or if the other side appears to be an
1154*27649Sminshall 	 * old 4.2 client (and thus unable to survive TCP urgent data),
1155*27649Sminshall 	 * write the entire buffer in non-OOB mode.
1156*27649Sminshall 	 */
1157*27649Sminshall 	if ((neturg == 0) || (not42 == 0)) {
115827185Sminshall 	    n = write(net, nbackp, n);	/* normal write */
115927185Sminshall 	} else {
116027185Sminshall 	    n = neturg - nbackp;
116127185Sminshall 	    /*
116227185Sminshall 	     * In 4.2 (and 4.3) systems, there is some question about
116327185Sminshall 	     * what byte in a sendOOB operation is the "OOB" data.
116427185Sminshall 	     * To make ourselves compatible, we only send ONE byte
116527185Sminshall 	     * out of band, the one WE THINK should be OOB (though
116627185Sminshall 	     * we really have more the TCP philosophy of urgent data
116727185Sminshall 	     * rather than the Unix philosophy of OOB data).
116827185Sminshall 	     */
116927185Sminshall 	    if (n > 1) {
117027185Sminshall 		n = send(net, nbackp, n-1, 0);	/* send URGENT all by itself */
117127185Sminshall 	    } else {
117227185Sminshall 		n = send(net, nbackp, n, MSG_OOB);	/* URGENT data */
117327185Sminshall 	    }
117427185Sminshall 	}
117527185Sminshall     }
117627185Sminshall     if (n < 0) {
117727185Sminshall 	if (errno == EWOULDBLOCK)
117827185Sminshall 	    return;
117927185Sminshall 	/* should blow this guy away... */
118027185Sminshall 	return;
118127185Sminshall     }
118227185Sminshall     nbackp += n;
118327185Sminshall     if (nbackp >= neturg) {
118427185Sminshall 	neturg = 0;
118527185Sminshall     }
118627185Sminshall     if (nbackp == nfrontp) {
118727185Sminshall 	nbackp = nfrontp = netobuf;
118827185Sminshall     }
118927185Sminshall }
119027185Sminshall 
11916002Sroot cleanup()
11926002Sroot {
11936002Sroot 
11946002Sroot 	rmut();
119510008Ssam 	vhangup();	/* XXX */
119610191Ssam 	shutdown(net, 2);
11976002Sroot 	exit(1);
11986002Sroot }
11996002Sroot 
12006002Sroot #include <utmp.h>
12016002Sroot 
12026002Sroot struct	utmp wtmp;
12036002Sroot char	wtmpf[]	= "/usr/adm/wtmp";
120423567Sbloom char	utmpf[] = "/etc/utmp";
120523567Sbloom #define SCPYN(a, b)	strncpy(a, b, sizeof(a))
120623567Sbloom #define SCMPN(a, b)	strncmp(a, b, sizeof(a))
12076002Sroot 
12086002Sroot rmut()
12096002Sroot {
12106002Sroot 	register f;
12116002Sroot 	int found = 0;
121223567Sbloom 	struct utmp *u, *utmp;
121323567Sbloom 	int nutmp;
121423567Sbloom 	struct stat statbf;
12156002Sroot 
121623567Sbloom 	f = open(utmpf, O_RDWR);
12176002Sroot 	if (f >= 0) {
121823567Sbloom 		fstat(f, &statbf);
121923567Sbloom 		utmp = (struct utmp *)malloc(statbf.st_size);
122023567Sbloom 		if (!utmp)
122123567Sbloom 			syslog(LOG_ERR, "utmp malloc failed");
122223567Sbloom 		if (statbf.st_size && utmp) {
122323567Sbloom 			nutmp = read(f, utmp, statbf.st_size);
122423567Sbloom 			nutmp /= sizeof(struct utmp);
122523567Sbloom 
122623567Sbloom 			for (u = utmp ; u < &utmp[nutmp] ; u++) {
122723567Sbloom 				if (SCMPN(u->ut_line, line+5) ||
122823567Sbloom 				    u->ut_name[0]==0)
122923567Sbloom 					continue;
123023567Sbloom 				lseek(f, ((long)u)-((long)utmp), L_SET);
123123567Sbloom 				SCPYN(u->ut_name, "");
123223567Sbloom 				SCPYN(u->ut_host, "");
123323567Sbloom 				time(&u->ut_time);
123423567Sbloom 				write(f, (char *)u, sizeof(wtmp));
123523567Sbloom 				found++;
123623567Sbloom 			}
12376002Sroot 		}
12386002Sroot 		close(f);
12396002Sroot 	}
12406002Sroot 	if (found) {
124117583Ssam 		f = open(wtmpf, O_WRONLY|O_APPEND);
12426002Sroot 		if (f >= 0) {
12436002Sroot 			SCPYN(wtmp.ut_line, line+5);
12446002Sroot 			SCPYN(wtmp.ut_name, "");
124512683Ssam 			SCPYN(wtmp.ut_host, "");
12466002Sroot 			time(&wtmp.ut_time);
124723567Sbloom 			write(f, (char *)&wtmp, sizeof(wtmp));
12486002Sroot 			close(f);
12496002Sroot 		}
12506002Sroot 	}
12516002Sroot 	chmod(line, 0666);
12526002Sroot 	chown(line, 0, 0);
12536002Sroot 	line[strlen("/dev/")] = 'p';
12546002Sroot 	chmod(line, 0666);
12556002Sroot 	chown(line, 0, 0);
12566002Sroot }
1257