xref: /csrg-svn/libexec/telnetd/telnetd.c (revision 34778)
121182Sdist /*
233687Sbostic  * Copyright (c) 1983, 1986 Regents of the University of California.
333687Sbostic  * All rights reserved.
433687Sbostic  *
533687Sbostic  * Redistribution and use in source and binary forms are permitted
6*34778Sbostic  * provided that the above copyright notice and this paragraph are
7*34778Sbostic  * duplicated in all such forms and that any documentation,
8*34778Sbostic  * advertising materials, and other materials related to such
9*34778Sbostic  * distribution and use acknowledge that the software was developed
10*34778Sbostic  * by the University of California, Berkeley.  The name of the
11*34778Sbostic  * University may not be used to endorse or promote products derived
12*34778Sbostic  * from this software without specific prior written permission.
13*34778Sbostic  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14*34778Sbostic  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15*34778Sbostic  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1621182Sdist  */
1721182Sdist 
186295Sroot #ifndef lint
1921182Sdist char copyright[] =
2033687Sbostic "@(#) Copyright (c) 1983, 1986 Regents of the University of California.\n\
2121182Sdist  All rights reserved.\n";
2233687Sbostic #endif /* not lint */
236295Sroot 
2421182Sdist #ifndef lint
25*34778Sbostic static char sccsid[] = "@(#)telnetd.c	5.29 (Berkeley) 06/18/88";
2633687Sbostic #endif /* not lint */
2721182Sdist 
286002Sroot /*
2927898Skarels  * Telnet server.
306002Sroot  */
3127898Skarels #include <sys/param.h>
329218Ssam #include <sys/socket.h>
3313608Ssam #include <sys/wait.h>
3417583Ssam #include <sys/file.h>
3520188Skarels #include <sys/stat.h>
3627185Sminshall #include <sys/time.h>
379218Ssam 
389218Ssam #include <netinet/in.h>
399218Ssam 
4012216Ssam #include <arpa/telnet.h>
4112216Ssam 
426002Sroot #include <stdio.h>
436002Sroot #include <signal.h>
446002Sroot #include <errno.h>
456002Sroot #include <sgtty.h>
468346Ssam #include <netdb.h>
4717187Sralph #include <syslog.h>
4827649Sminshall #include <ctype.h>
499218Ssam 
5027983Sminshall #define	OPT_NO			0		/* won't do this option */
5127983Sminshall #define	OPT_YES			1		/* will do this option */
5227983Sminshall #define	OPT_YES_BUT_ALWAYS_LOOK	2
5327983Sminshall #define	OPT_NO_BUT_ALWAYS_LOOK	3
546002Sroot char	hisopts[256];
556002Sroot char	myopts[256];
566002Sroot 
576002Sroot char	doopt[] = { IAC, DO, '%', 'c', 0 };
586002Sroot char	dont[] = { IAC, DONT, '%', 'c', 0 };
596002Sroot char	will[] = { IAC, WILL, '%', 'c', 0 };
606002Sroot char	wont[] = { IAC, WONT, '%', 'c', 0 };
616002Sroot 
626002Sroot /*
636002Sroot  * I/O data buffers, pointers, and counters.
646002Sroot  */
656002Sroot char	ptyibuf[BUFSIZ], *ptyip = ptyibuf;
6627649Sminshall 
676002Sroot char	ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf;
6827649Sminshall 
696002Sroot char	netibuf[BUFSIZ], *netip = netibuf;
7027649Sminshall #define	NIACCUM(c)	{   *netip++ = c; \
7127649Sminshall 			    ncc++; \
7227649Sminshall 			}
7327649Sminshall 
746388Ssam char	netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf;
7527185Sminshall char	*neturg = 0;		/* one past last bye of urgent data */
7627649Sminshall 	/* the remote system seems to NOT be an old 4.2 */
7727649Sminshall int	not42 = 1;
7827649Sminshall 
7933271Sminshall #define	BANNER	"\r\n\r\n4.3 BSD UNIX (%s)\r\n\r\r\n\r"
8027649Sminshall 
8127983Sminshall 		/* buffer for sub-options */
8227983Sminshall char	subbuffer[100], *subpointer= subbuffer, *subend= subbuffer;
8327649Sminshall #define	SB_CLEAR()	subpointer = subbuffer;
8427983Sminshall #define	SB_TERM()	{ subend = subpointer; SB_CLEAR(); }
8527649Sminshall #define	SB_ACCUM(c)	if (subpointer < (subbuffer+sizeof subbuffer)) { \
8627649Sminshall 				*subpointer++ = (c); \
8727649Sminshall 			}
8827983Sminshall #define	SB_GET()	((*subpointer++)&0xff)
8927983Sminshall #define	SB_EOF()	(subpointer >= subend)
9027649Sminshall 
916002Sroot int	pcc, ncc;
926002Sroot 
936002Sroot int	pty, net;
946002Sroot int	inter;
9513799Ssam extern	char **environ;
966002Sroot extern	int errno;
9720188Skarels char	*line;
9827185Sminshall int	SYNCHing = 0;		/* we are in TELNET SYNCH mode */
9927185Sminshall /*
10027185Sminshall  * The following are some clocks used to decide how to interpret
10127185Sminshall  * the relationship between various variables.
10227185Sminshall  */
1036002Sroot 
10427185Sminshall struct {
10527185Sminshall     int
10627185Sminshall 	system,			/* what the current time is */
10727185Sminshall 	echotoggle,		/* last time user entered echo character */
10827185Sminshall 	modenegotiated,		/* last time operating mode negotiated */
10927185Sminshall 	didnetreceive,		/* last time we read data from network */
11027983Sminshall 	ttypeopt,		/* ttype will/won't received */
11127983Sminshall 	ttypesubopt,		/* ttype subopt is received */
11227983Sminshall 	getterminal,		/* time started to get terminal information */
11327185Sminshall 	gotDM;			/* when did we last see a data mark */
11427185Sminshall } clocks;
11527185Sminshall 
11627983Sminshall #define	settimer(x)	(clocks.x = ++clocks.system)
11727983Sminshall #define	sequenceIs(x,y)	(clocks.x < clocks.y)
11827185Sminshall 
1196002Sroot main(argc, argv)
1206002Sroot 	char *argv[];
1216002Sroot {
12216371Skarels 	struct sockaddr_in from;
12317156Ssam 	int on = 1, fromlen;
1246002Sroot 
12527185Sminshall #if	defined(DEBUG)
12627185Sminshall 	{
12727185Sminshall 	    int s, ns, foo;
12827185Sminshall 	    struct servent *sp;
12927185Sminshall 	    static struct sockaddr_in sin = { AF_INET };
13027185Sminshall 
13127185Sminshall 	    sp = getservbyname("telnet", "tcp");
13227185Sminshall 	    if (sp == 0) {
13327185Sminshall 		    fprintf(stderr, "telnetd: tcp/telnet: unknown service\n");
13427185Sminshall 		    exit(1);
13527185Sminshall 	    }
13627185Sminshall 	    sin.sin_port = sp->s_port;
13727185Sminshall 	    argc--, argv++;
13827185Sminshall 	    if (argc > 0) {
13927185Sminshall 		    sin.sin_port = atoi(*argv);
14027185Sminshall 		    sin.sin_port = htons((u_short)sin.sin_port);
14127185Sminshall 	    }
14227185Sminshall 
14327185Sminshall 	    s = socket(AF_INET, SOCK_STREAM, 0);
14427185Sminshall 	    if (s < 0) {
14527185Sminshall 		    perror("telnetd: socket");;
14627185Sminshall 		    exit(1);
14727185Sminshall 	    }
14827185Sminshall 	    if (bind(s, &sin, sizeof sin) < 0) {
14927185Sminshall 		perror("bind");
15027185Sminshall 		exit(1);
15127185Sminshall 	    }
15227185Sminshall 	    if (listen(s, 1) < 0) {
15327185Sminshall 		perror("listen");
15427185Sminshall 		exit(1);
15527185Sminshall 	    }
15627185Sminshall 	    foo = sizeof sin;
15727185Sminshall 	    ns = accept(s, &sin, &foo);
15827185Sminshall 	    if (ns < 0) {
15927185Sminshall 		perror("accept");
16027185Sminshall 		exit(1);
16127185Sminshall 	    }
16227185Sminshall 	    dup2(ns, 0);
16327185Sminshall 	    close(s);
16427185Sminshall 	}
16527185Sminshall #endif	/* defined(DEBUG) */
16624855Seric 	openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON);
16716371Skarels 	fromlen = sizeof (from);
16816371Skarels 	if (getpeername(0, &from, &fromlen) < 0) {
16916371Skarels 		fprintf(stderr, "%s: ", argv[0]);
17016371Skarels 		perror("getpeername");
17116371Skarels 		_exit(1);
1728346Ssam 	}
17317156Ssam 	if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) {
17417187Sralph 		syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
17510418Ssam 	}
17616371Skarels 	doit(0, &from);
1776002Sroot }
1786002Sroot 
17927983Sminshall char	*terminaltype = 0;
18027983Sminshall char	*envinit[2];
18127983Sminshall int	cleanup();
18227649Sminshall 
18327649Sminshall /*
18427983Sminshall  * ttloop
18527649Sminshall  *
18627983Sminshall  *	A small subroutine to flush the network output buffer, get some data
18727983Sminshall  * from the network, and pass it through the telnet state machine.  We
18827983Sminshall  * also flush the pty input buffer (by dropping its data) if it becomes
18927983Sminshall  * too full.
19027983Sminshall  */
19127983Sminshall 
19227983Sminshall void
19327983Sminshall ttloop()
19427983Sminshall {
19527983Sminshall     if (nfrontp-nbackp) {
19627983Sminshall 	netflush();
19727983Sminshall     }
19827983Sminshall     ncc = read(net, netibuf, sizeof netibuf);
19927983Sminshall     if (ncc < 0) {
20027983Sminshall 	syslog(LOG_INFO, "ttloop:  read: %m\n");
20128044Sminshall 	exit(1);
20228044Sminshall     } else if (ncc == 0) {
20328044Sminshall 	syslog(LOG_INFO, "ttloop:  peer died: %m\n");
20428044Sminshall 	exit(1);
20527983Sminshall     }
20627983Sminshall     netip = netibuf;
20727983Sminshall     telrcv();			/* state machine */
20827983Sminshall     if (ncc > 0) {
20927983Sminshall 	pfrontp = pbackp = ptyobuf;
21027983Sminshall 	telrcv();
21127983Sminshall     }
21227983Sminshall }
21327983Sminshall 
21427983Sminshall /*
21527983Sminshall  * getterminaltype
21627649Sminshall  *
21727983Sminshall  *	Ask the other end to send along its terminal type.
21827983Sminshall  * Output is the variable terminaltype filled in.
21927649Sminshall  */
22027649Sminshall 
22127983Sminshall void
22227983Sminshall getterminaltype()
22327649Sminshall {
22427983Sminshall     static char sbuf[] = { IAC, DO, TELOPT_TTYPE };
22527649Sminshall 
22627983Sminshall     settimer(getterminal);
22727983Sminshall     bcopy(sbuf, nfrontp, sizeof sbuf);
22827983Sminshall     nfrontp += sizeof sbuf;
22928044Sminshall     hisopts[TELOPT_TTYPE] = OPT_YES_BUT_ALWAYS_LOOK;
23027983Sminshall     while (sequenceIs(ttypeopt, getterminal)) {
23127983Sminshall 	ttloop();
23227649Sminshall     }
23327983Sminshall     if (hisopts[TELOPT_TTYPE] == OPT_YES) {
23427983Sminshall 	static char sbbuf[] = { IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE };
23527983Sminshall 
23627983Sminshall 	bcopy(sbbuf, nfrontp, sizeof sbbuf);
23727983Sminshall 	nfrontp += sizeof sbbuf;
23827983Sminshall 	while (sequenceIs(ttypesubopt, getterminal)) {
23927983Sminshall 	    ttloop();
24027983Sminshall 	}
24127983Sminshall     }
24227649Sminshall }
24327649Sminshall 
2446002Sroot /*
2456002Sroot  * Get a pty, scan input lines.
2466002Sroot  */
24712683Ssam doit(f, who)
24812683Ssam 	int f;
24912683Ssam 	struct sockaddr_in *who;
2506002Sroot {
25120188Skarels 	char *host, *inet_ntoa();
25217583Ssam 	int i, p, t;
2536002Sroot 	struct sgttyb b;
25412683Ssam 	struct hostent *hp;
25527649Sminshall 	int c;
2566002Sroot 
25720188Skarels 	for (c = 'p'; c <= 's'; c++) {
25820188Skarels 		struct stat stb;
25920188Skarels 
26020188Skarels 		line = "/dev/ptyXX";
26120188Skarels 		line[strlen("/dev/pty")] = c;
26220188Skarels 		line[strlen("/dev/ptyp")] = '0';
26320188Skarels 		if (stat(line, &stb) < 0)
26420188Skarels 			break;
26517583Ssam 		for (i = 0; i < 16; i++) {
26634424Sbostic 			line[sizeof("/dev/ptyp") - 1] = "0123456789abcdef"[i];
26734424Sbostic 			p = open(line, O_RDWR);
26817583Ssam 			if (p > 0)
26917583Ssam 				goto gotpty;
27017583Ssam 		}
2716002Sroot 	}
2729244Ssam 	fatal(f, "All network ports in use");
2739244Ssam 	/*NOTREACHED*/
2746002Sroot gotpty:
2756002Sroot 	dup2(f, 0);
27620188Skarels 	line[strlen("/dev/")] = 't';
27717583Ssam 	t = open("/dev/tty", O_RDWR);
2786002Sroot 	if (t >= 0) {
2796002Sroot 		ioctl(t, TIOCNOTTY, 0);
2806002Sroot 		close(t);
2816002Sroot 	}
28220188Skarels 	t = open(line, O_RDWR);
2839244Ssam 	if (t < 0)
28434424Sbostic 		fatalperror(f, line);
28534424Sbostic 	if (fchmod(t, 0))
28634424Sbostic 		fatalperror(f, line);
28734424Sbostic 	(void)signal(SIGHUP, SIG_IGN);
28834424Sbostic 	vhangup();
28934424Sbostic 	(void)signal(SIGHUP, SIG_DFL);
29034424Sbostic 	t = open(line, O_RDWR);
29134424Sbostic 	if (t < 0)
29234424Sbostic 		fatalperror(f, line);
2936002Sroot 	ioctl(t, TIOCGETP, &b);
2946388Ssam 	b.sg_flags = CRMOD|XTABS|ANYP;
2956002Sroot 	ioctl(t, TIOCSETP, &b);
2966388Ssam 	ioctl(p, TIOCGETP, &b);
2978379Ssam 	b.sg_flags &= ~ECHO;
2986388Ssam 	ioctl(p, TIOCSETP, &b);
29912683Ssam 	hp = gethostbyaddr(&who->sin_addr, sizeof (struct in_addr),
30012683Ssam 		who->sin_family);
30112683Ssam 	if (hp)
30212683Ssam 		host = hp->h_name;
30312683Ssam 	else
30417444Sralph 		host = inet_ntoa(who->sin_addr);
30527983Sminshall 
30627983Sminshall 	net = f;
30727983Sminshall 	pty = p;
30827983Sminshall 
30927983Sminshall 	/*
31027983Sminshall 	 * get terminal type.
31127983Sminshall 	 */
31227983Sminshall 	getterminaltype();
31327983Sminshall 
3149244Ssam 	if ((i = fork()) < 0)
31534424Sbostic 		fatalperror(f, "fork");
3166002Sroot 	if (i)
3176002Sroot 		telnet(f, p);
3186002Sroot 	close(f);
3196002Sroot 	close(p);
3206002Sroot 	dup2(t, 0);
3216002Sroot 	dup2(t, 1);
3226002Sroot 	dup2(t, 2);
3236002Sroot 	close(t);
32427983Sminshall 	envinit[0] = terminaltype;
32527983Sminshall 	envinit[1] = 0;
32613799Ssam 	environ = envinit;
32727649Sminshall 	/*
32827649Sminshall 	 * -h : pass on name of host.
32927983Sminshall 	 *		WARNING:  -h is accepted by login if and only if
33027983Sminshall 	 *			getuid() == 0.
33127649Sminshall 	 * -p : don't clobber the environment (so terminal type stays set).
33227649Sminshall 	 */
33327649Sminshall 	execl("/bin/login", "login", "-h", host,
33427983Sminshall 					terminaltype ? "-p" : 0, 0);
33534424Sbostic 	fatalperror(f, "/bin/login");
3369244Ssam 	/*NOTREACHED*/
3379244Ssam }
3389244Ssam 
3399244Ssam fatal(f, msg)
3409244Ssam 	int f;
3419244Ssam 	char *msg;
3429244Ssam {
3439244Ssam 	char buf[BUFSIZ];
3449244Ssam 
34517583Ssam 	(void) sprintf(buf, "telnetd: %s.\r\n", msg);
3469244Ssam 	(void) write(f, buf, strlen(buf));
3476002Sroot 	exit(1);
3486002Sroot }
3496002Sroot 
35034424Sbostic fatalperror(f, msg)
3519244Ssam 	int f;
3529244Ssam 	char *msg;
3539244Ssam {
3549244Ssam 	char buf[BUFSIZ];
3559244Ssam 	extern char *sys_errlist[];
3569244Ssam 
35717583Ssam 	(void) sprintf(buf, "%s: %s\r\n", msg, sys_errlist[errno]);
3589244Ssam 	fatal(f, buf);
3599244Ssam }
3609244Ssam 
36127185Sminshall 
3626002Sroot /*
36327185Sminshall  * Check a descriptor to see if out of band data exists on it.
36427185Sminshall  */
36527185Sminshall 
36627185Sminshall 
36727185Sminshall stilloob(s)
36827185Sminshall int	s;		/* socket number */
36927185Sminshall {
37027185Sminshall     static struct timeval timeout = { 0 };
37127185Sminshall     fd_set	excepts;
37227185Sminshall     int value;
37327185Sminshall 
37427185Sminshall     do {
37527185Sminshall 	FD_ZERO(&excepts);
37627185Sminshall 	FD_SET(s, &excepts);
37727185Sminshall 	value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout);
37827898Skarels     } while ((value == -1) && (errno == EINTR));
37927185Sminshall 
38027185Sminshall     if (value < 0) {
38134424Sbostic 	fatalperror(pty, "select");
38227185Sminshall     }
38327185Sminshall     if (FD_ISSET(s, &excepts)) {
38427185Sminshall 	return 1;
38527185Sminshall     } else {
38627185Sminshall 	return 0;
38727185Sminshall     }
38827185Sminshall }
38927185Sminshall 
39027185Sminshall /*
3916002Sroot  * Main loop.  Select from pty and network, and
3926002Sroot  * hand data to telnet receiver finite state machine.
3936002Sroot  */
3946002Sroot telnet(f, p)
3956002Sroot {
3966002Sroot 	int on = 1;
39727898Skarels 	char hostname[MAXHOSTNAMELEN];
39833271Sminshall #define	TABBUFSIZ	512
39933271Sminshall 	char	defent[TABBUFSIZ];
40033271Sminshall 	char	defstrs[TABBUFSIZ];
40133271Sminshall #undef	TABBUFSIZ
40233271Sminshall 	char *HE;
40333271Sminshall 	char *HN;
40433271Sminshall 	char *IM;
4056002Sroot 
4066002Sroot 	ioctl(f, FIONBIO, &on);
4076002Sroot 	ioctl(p, FIONBIO, &on);
40833267Sminshall 	ioctl(p, TIOCPKT, &on);
40927649Sminshall #if	defined(SO_OOBINLINE)
41027649Sminshall 	setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on);
41127649Sminshall #endif	/* defined(SO_OOBINLINE) */
4126002Sroot 	signal(SIGTSTP, SIG_IGN);
41332400Sminshall 	/*
41432400Sminshall 	 * Ignoring SIGTTOU keeps the kernel from blocking us
41532400Sminshall 	 * in ttioctl() in /sys/tty.c.
41632400Sminshall 	 */
41732400Sminshall 	signal(SIGTTOU, SIG_IGN);
41813028Ssam 	signal(SIGCHLD, cleanup);
41926083Slepreau 	setpgrp(0, 0);
4206002Sroot 
4218379Ssam 	/*
42227185Sminshall 	 * Request to do remote echo and to suppress go ahead.
4238379Ssam 	 */
42427983Sminshall 	if (!myopts[TELOPT_ECHO]) {
42527983Sminshall 	    dooption(TELOPT_ECHO);
42627983Sminshall 	}
42727983Sminshall 	if (!myopts[TELOPT_SGA]) {
42827983Sminshall 	    dooption(TELOPT_SGA);
42927983Sminshall 	}
43012713Ssam 	/*
43127649Sminshall 	 * Is the client side a 4.2 (NOT 4.3) system?  We need to know this
43227649Sminshall 	 * because 4.2 clients are unable to deal with TCP urgent data.
43327649Sminshall 	 *
43427649Sminshall 	 * To find out, we send out a "DO ECHO".  If the remote system
43527649Sminshall 	 * answers "WILL ECHO" it is probably a 4.2 client, and we note
43627649Sminshall 	 * that fact ("WILL ECHO" ==> that the client will echo what
43727649Sminshall 	 * WE, the server, sends it; it does NOT mean that the client will
43827649Sminshall 	 * echo the terminal input).
43927649Sminshall 	 */
44032452Sbostic 	(void) sprintf(nfrontp, doopt, TELOPT_ECHO);
44127649Sminshall 	nfrontp += sizeof doopt-2;
44227983Sminshall 	hisopts[TELOPT_ECHO] = OPT_YES_BUT_ALWAYS_LOOK;
44327649Sminshall 
44427649Sminshall 	/*
44512713Ssam 	 * Show banner that getty never gave.
44627797Sminshall 	 *
44733271Sminshall 	 * We put the banner in the pty input buffer.  This way, it
44833271Sminshall 	 * gets carriage return null processing, etc., just like all
44933271Sminshall 	 * other pty --> client data.
45012713Ssam 	 */
45127797Sminshall 
45212713Ssam 	gethostname(hostname, sizeof (hostname));
45333271Sminshall 	if (getent(defent, "default") == 1) {
45433271Sminshall 		char *getstr();
45533271Sminshall 		char *p=defstrs;
45627649Sminshall 
45733271Sminshall 		HE = getstr("he", &p);
45833271Sminshall 		HN = getstr("hn", &p);
45933271Sminshall 		IM = getstr("im", &p);
46033271Sminshall 		if (HN && *HN)
46133271Sminshall 			strcpy(hostname, HN);
46233271Sminshall 		edithost(HE, hostname);
46333271Sminshall 		if (IM && *IM)
46434424Sbostic 			putf(IM, ptyibuf+1);
46533271Sminshall 	} else {
46633271Sminshall 		sprintf(ptyibuf+1, BANNER, hostname);
46733271Sminshall 	}
46827797Sminshall 
46933271Sminshall 	ptyip = ptyibuf+1;		/* Prime the pump */
47033271Sminshall 	pcc = strlen(ptyip);		/* ditto */
47133271Sminshall 
47233267Sminshall 	/* Clear ptybuf[0] - where the packet information is received */
47333267Sminshall 	ptyibuf[0] = 0;
47433271Sminshall 
47527649Sminshall 	/*
47627649Sminshall 	 * Call telrcv() once to pick up anything received during
47727649Sminshall 	 * terminal type negotiation.
47827649Sminshall 	 */
47927649Sminshall 	telrcv();
48027649Sminshall 
4816002Sroot 	for (;;) {
48227185Sminshall 		fd_set ibits, obits, xbits;
4836002Sroot 		register int c;
4846002Sroot 
48527185Sminshall 		if (ncc < 0 && pcc < 0)
48627185Sminshall 			break;
48727185Sminshall 
48827185Sminshall 		FD_ZERO(&ibits);
48927185Sminshall 		FD_ZERO(&obits);
49027185Sminshall 		FD_ZERO(&xbits);
4916002Sroot 		/*
4926002Sroot 		 * Never look for input if there's still
4936002Sroot 		 * stuff in the corresponding output buffer
4946002Sroot 		 */
49527185Sminshall 		if (nfrontp - nbackp || pcc > 0) {
49627185Sminshall 			FD_SET(f, &obits);
49733267Sminshall 			FD_SET(p, &xbits);
49827185Sminshall 		} else {
49927185Sminshall 			FD_SET(p, &ibits);
50027185Sminshall 		}
50127185Sminshall 		if (pfrontp - pbackp || ncc > 0) {
50227185Sminshall 			FD_SET(p, &obits);
50327185Sminshall 		} else {
50427185Sminshall 			FD_SET(f, &ibits);
50527185Sminshall 		}
50627185Sminshall 		if (!SYNCHing) {
50727185Sminshall 			FD_SET(f, &xbits);
50827185Sminshall 		}
50927185Sminshall 		if ((c = select(16, &ibits, &obits, &xbits,
51027185Sminshall 						(struct timeval *)0)) < 1) {
51127185Sminshall 			if (c == -1) {
51227185Sminshall 				if (errno == EINTR) {
51327185Sminshall 					continue;
51427185Sminshall 				}
51527185Sminshall 			}
5166002Sroot 			sleep(5);
5176002Sroot 			continue;
5186002Sroot 		}
5196002Sroot 
5206002Sroot 		/*
52127185Sminshall 		 * Any urgent data?
52227185Sminshall 		 */
52327185Sminshall 		if (FD_ISSET(net, &xbits)) {
52427185Sminshall 		    SYNCHing = 1;
52527185Sminshall 		}
52627185Sminshall 
52727185Sminshall 		/*
5286002Sroot 		 * Something to read from the network...
5296002Sroot 		 */
53027185Sminshall 		if (FD_ISSET(net, &ibits)) {
53127649Sminshall #if	!defined(SO_OOBINLINE)
53227185Sminshall 			/*
53327898Skarels 			 * In 4.2 (and 4.3 beta) systems, the
53427185Sminshall 			 * OOB indication and data handling in the kernel
53527185Sminshall 			 * is such that if two separate TCP Urgent requests
53627185Sminshall 			 * come in, one byte of TCP data will be overlaid.
53727185Sminshall 			 * This is fatal for Telnet, but we try to live
53827185Sminshall 			 * with it.
53927185Sminshall 			 *
54027185Sminshall 			 * In addition, in 4.2 (and...), a special protocol
54127185Sminshall 			 * is needed to pick up the TCP Urgent data in
54227185Sminshall 			 * the correct sequence.
54327185Sminshall 			 *
54427185Sminshall 			 * What we do is:  if we think we are in urgent
54527185Sminshall 			 * mode, we look to see if we are "at the mark".
54627185Sminshall 			 * If we are, we do an OOB receive.  If we run
54727185Sminshall 			 * this twice, we will do the OOB receive twice,
54827185Sminshall 			 * but the second will fail, since the second
54927185Sminshall 			 * time we were "at the mark", but there wasn't
55027185Sminshall 			 * any data there (the kernel doesn't reset
55127185Sminshall 			 * "at the mark" until we do a normal read).
55227185Sminshall 			 * Once we've read the OOB data, we go ahead
55327185Sminshall 			 * and do normal reads.
55427185Sminshall 			 *
55527185Sminshall 			 * There is also another problem, which is that
55627185Sminshall 			 * since the OOB byte we read doesn't put us
55727185Sminshall 			 * out of OOB state, and since that byte is most
55827185Sminshall 			 * likely the TELNET DM (data mark), we would
55927185Sminshall 			 * stay in the TELNET SYNCH (SYNCHing) state.
56027185Sminshall 			 * So, clocks to the rescue.  If we've "just"
56127185Sminshall 			 * received a DM, then we test for the
56227185Sminshall 			 * presence of OOB data when the receive OOB
56327185Sminshall 			 * fails (and AFTER we did the normal mode read
56427185Sminshall 			 * to clear "at the mark").
56527185Sminshall 			 */
56627185Sminshall 		    if (SYNCHing) {
56727185Sminshall 			int atmark;
56827185Sminshall 
56927185Sminshall 			ioctl(net, SIOCATMARK, (char *)&atmark);
57027185Sminshall 			if (atmark) {
57127185Sminshall 			    ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB);
57227185Sminshall 			    if ((ncc == -1) && (errno == EINVAL)) {
57327185Sminshall 				ncc = read(net, netibuf, sizeof (netibuf));
57427983Sminshall 				if (sequenceIs(didnetreceive, gotDM)) {
57527185Sminshall 				    SYNCHing = stilloob(net);
57627185Sminshall 				}
57727185Sminshall 			    }
57827185Sminshall 			} else {
57927185Sminshall 			    ncc = read(net, netibuf, sizeof (netibuf));
5806002Sroot 			}
58127185Sminshall 		    } else {
58227185Sminshall 			ncc = read(net, netibuf, sizeof (netibuf));
58327185Sminshall 		    }
58427185Sminshall 		    settimer(didnetreceive);
58527649Sminshall #else	/* !defined(SO_OOBINLINE)) */
58627185Sminshall 		    ncc = read(net, netibuf, sizeof (netibuf));
58727649Sminshall #endif	/* !defined(SO_OOBINLINE)) */
58827185Sminshall 		    if (ncc < 0 && errno == EWOULDBLOCK)
58927185Sminshall 			ncc = 0;
59027185Sminshall 		    else {
59127185Sminshall 			if (ncc <= 0) {
59227185Sminshall 			    break;
59327185Sminshall 			}
59427185Sminshall 			netip = netibuf;
59527185Sminshall 		    }
5966002Sroot 		}
5976002Sroot 
5986002Sroot 		/*
5996002Sroot 		 * Something to read from the pty...
6006002Sroot 		 */
60133267Sminshall 		if (FD_ISSET(p, &xbits)) {
60233267Sminshall 			if (read(p, ptyibuf, 1) != 1) {
60333267Sminshall 				break;
60433267Sminshall 			}
60533267Sminshall 		}
60627185Sminshall 		if (FD_ISSET(p, &ibits)) {
6076002Sroot 			pcc = read(p, ptyibuf, BUFSIZ);
6086002Sroot 			if (pcc < 0 && errno == EWOULDBLOCK)
6096002Sroot 				pcc = 0;
6106002Sroot 			else {
6116002Sroot 				if (pcc <= 0)
6126002Sroot 					break;
61333267Sminshall 				/* Skip past "packet" */
61433267Sminshall 				pcc--;
61533267Sminshall 				ptyip = ptyibuf+1;
6166002Sroot 			}
6176002Sroot 		}
61833267Sminshall 		if (ptyibuf[0] & TIOCPKT_FLUSHWRITE) {
61933267Sminshall 			netclear();	/* clear buffer back */
62033267Sminshall 			*nfrontp++ = IAC;
62133267Sminshall 			*nfrontp++ = DM;
62233267Sminshall 			neturg = nfrontp-1;  /* off by one XXX */
62333267Sminshall 			ptyibuf[0] = 0;
62433267Sminshall 		}
6256002Sroot 
6266002Sroot 		while (pcc > 0) {
6276002Sroot 			if ((&netobuf[BUFSIZ] - nfrontp) < 2)
6286002Sroot 				break;
6296002Sroot 			c = *ptyip++ & 0377, pcc--;
6306002Sroot 			if (c == IAC)
6316002Sroot 				*nfrontp++ = c;
6326002Sroot 			*nfrontp++ = c;
63331940Sbostic 			/* Don't do CR-NUL if we are in binary mode */
63431940Sbostic 			if ((c == '\r') && (myopts[TELOPT_BINARY] == OPT_NO)) {
63527020Sminshall 				if (pcc > 0 && ((*ptyip & 0377) == '\n')) {
63627020Sminshall 					*nfrontp++ = *ptyip++ & 0377;
63727020Sminshall 					pcc--;
63827020Sminshall 				} else
63927020Sminshall 					*nfrontp++ = '\0';
64027020Sminshall 			}
6416002Sroot 		}
64227185Sminshall 		if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0)
6436002Sroot 			netflush();
6446002Sroot 		if (ncc > 0)
6456002Sroot 			telrcv();
64627185Sminshall 		if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0)
6476002Sroot 			ptyflush();
6486002Sroot 	}
6496002Sroot 	cleanup();
6506002Sroot }
6516002Sroot 
6526002Sroot /*
6536002Sroot  * State for recv fsm
6546002Sroot  */
6556002Sroot #define	TS_DATA		0	/* base state */
6566002Sroot #define	TS_IAC		1	/* look for double IAC's */
6576002Sroot #define	TS_CR		2	/* CR-LF ->'s CR */
65827649Sminshall #define	TS_SB		3	/* throw away begin's... */
65927649Sminshall #define	TS_SE		4	/* ...end's (suboption negotiation) */
6606002Sroot #define	TS_WILL		5	/* will option negotiation */
6616002Sroot #define	TS_WONT		6	/* wont " */
6626002Sroot #define	TS_DO		7	/* do " */
6636002Sroot #define	TS_DONT		8	/* dont " */
6646002Sroot 
6656002Sroot telrcv()
6666002Sroot {
6676002Sroot 	register int c;
6686002Sroot 	static int state = TS_DATA;
6696002Sroot 
6706002Sroot 	while (ncc > 0) {
6716002Sroot 		if ((&ptyobuf[BUFSIZ] - pfrontp) < 2)
6726002Sroot 			return;
6736002Sroot 		c = *netip++ & 0377, ncc--;
6746002Sroot 		switch (state) {
6756002Sroot 
67626090Sminshall 		case TS_CR:
67726090Sminshall 			state = TS_DATA;
67832097Sminshall 			/* Strip off \n or \0 after a \r */
67926499Sminshall 			if ((c == 0) || (c == '\n')) {
68026090Sminshall 				break;
68126499Sminshall 			}
68226090Sminshall 			/* FALL THROUGH */
68326090Sminshall 
6846002Sroot 		case TS_DATA:
6856002Sroot 			if (c == IAC) {
6866002Sroot 				state = TS_IAC;
6876002Sroot 				break;
6886002Sroot 			}
6896002Sroot 			if (inter > 0)
6906002Sroot 				break;
69127020Sminshall 			/*
69232097Sminshall 			 * We now map \r\n ==> \r for pragmatic reasons.
69332097Sminshall 			 * Many client implementations send \r\n when
69432097Sminshall 			 * the user hits the CarriageReturn key.
69532097Sminshall 			 *
69632097Sminshall 			 * We USED to map \r\n ==> \n, since \r\n says
69727020Sminshall 			 * that we want to be in column 1 of the next
69827020Sminshall 			 * printable line, and \n is the standard
69927020Sminshall 			 * unix way of saying that (\r is only good
70027020Sminshall 			 * if CRMOD is set, which it normally is).
70127020Sminshall 			 */
70231940Sbostic 			if ((c == '\r') && (hisopts[TELOPT_BINARY] == OPT_NO)) {
70332097Sminshall 				state = TS_CR;
70426499Sminshall 			}
70526499Sminshall 			*pfrontp++ = c;
7066002Sroot 			break;
7076002Sroot 
7086002Sroot 		case TS_IAC:
7096002Sroot 			switch (c) {
7106002Sroot 
7116002Sroot 			/*
7126002Sroot 			 * Send the process on the pty side an
7136002Sroot 			 * interrupt.  Do this with a NULL or
7146002Sroot 			 * interrupt char; depending on the tty mode.
7156002Sroot 			 */
7166002Sroot 			case IP:
7176002Sroot 				interrupt();
7186002Sroot 				break;
7196002Sroot 
72027229Sminshall 			case BREAK:
72127229Sminshall 				sendbrk();
72227229Sminshall 				break;
72327229Sminshall 
7246002Sroot 			/*
7256002Sroot 			 * Are You There?
7266002Sroot 			 */
7276002Sroot 			case AYT:
72817583Ssam 				strcpy(nfrontp, "\r\n[Yes]\r\n");
72917583Ssam 				nfrontp += 9;
7306002Sroot 				break;
7316002Sroot 
7326002Sroot 			/*
73327185Sminshall 			 * Abort Output
73427185Sminshall 			 */
73527185Sminshall 			case AO: {
73627185Sminshall 					struct ltchars tmpltc;
73727185Sminshall 
73827185Sminshall 					ptyflush();	/* half-hearted */
73927185Sminshall 					ioctl(pty, TIOCGLTC, &tmpltc);
74027185Sminshall 					if (tmpltc.t_flushc != '\377') {
74127185Sminshall 						*pfrontp++ = tmpltc.t_flushc;
74227185Sminshall 					}
74327260Sminshall 					netclear();	/* clear buffer back */
74427185Sminshall 					*nfrontp++ = IAC;
74527185Sminshall 					*nfrontp++ = DM;
74627187Sminshall 					neturg = nfrontp-1; /* off by one XXX */
74727185Sminshall 					break;
74827185Sminshall 				}
74927185Sminshall 
75027185Sminshall 			/*
7516002Sroot 			 * Erase Character and
7526002Sroot 			 * Erase Line
7536002Sroot 			 */
7546002Sroot 			case EC:
75527185Sminshall 			case EL: {
75627185Sminshall 					struct sgttyb b;
75727185Sminshall 					char ch;
7586002Sroot 
75927185Sminshall 					ptyflush();	/* half-hearted */
76027185Sminshall 					ioctl(pty, TIOCGETP, &b);
76127185Sminshall 					ch = (c == EC) ?
76227185Sminshall 						b.sg_erase : b.sg_kill;
76327185Sminshall 					if (ch != '\377') {
76427185Sminshall 						*pfrontp++ = ch;
76527185Sminshall 					}
76627185Sminshall 					break;
76727185Sminshall 				}
76827185Sminshall 
7696002Sroot 			/*
7706002Sroot 			 * Check for urgent data...
7716002Sroot 			 */
7726002Sroot 			case DM:
77327185Sminshall 				SYNCHing = stilloob(net);
77427185Sminshall 				settimer(gotDM);
7756002Sroot 				break;
7766002Sroot 
77727185Sminshall 
7786002Sroot 			/*
7796002Sroot 			 * Begin option subnegotiation...
7806002Sroot 			 */
7816002Sroot 			case SB:
78227649Sminshall 				state = TS_SB;
7836002Sroot 				continue;
7846002Sroot 
7856002Sroot 			case WILL:
78627188Sminshall 				state = TS_WILL;
78727188Sminshall 				continue;
78827188Sminshall 
7896002Sroot 			case WONT:
79027188Sminshall 				state = TS_WONT;
79127188Sminshall 				continue;
79227188Sminshall 
7936002Sroot 			case DO:
79427188Sminshall 				state = TS_DO;
79527188Sminshall 				continue;
79627188Sminshall 
7976002Sroot 			case DONT:
79827188Sminshall 				state = TS_DONT;
7996002Sroot 				continue;
8006002Sroot 
8016002Sroot 			case IAC:
8026002Sroot 				*pfrontp++ = c;
8036002Sroot 				break;
8046002Sroot 			}
8056002Sroot 			state = TS_DATA;
8066002Sroot 			break;
8076002Sroot 
80827649Sminshall 		case TS_SB:
80927649Sminshall 			if (c == IAC) {
81027649Sminshall 				state = TS_SE;
81127649Sminshall 			} else {
81227649Sminshall 				SB_ACCUM(c);
81327649Sminshall 			}
8146002Sroot 			break;
8156002Sroot 
81627649Sminshall 		case TS_SE:
81727649Sminshall 			if (c != SE) {
81827649Sminshall 				if (c != IAC) {
81927649Sminshall 					SB_ACCUM(IAC);
82027649Sminshall 				}
82127649Sminshall 				SB_ACCUM(c);
82227649Sminshall 				state = TS_SB;
82327649Sminshall 			} else {
82427649Sminshall 				SB_TERM();
82527649Sminshall 				suboption();	/* handle sub-option */
82627649Sminshall 				state = TS_DATA;
82727649Sminshall 			}
8286002Sroot 			break;
8296002Sroot 
8306002Sroot 		case TS_WILL:
83127983Sminshall 			if (hisopts[c] != OPT_YES)
8326002Sroot 				willoption(c);
8336002Sroot 			state = TS_DATA;
8346002Sroot 			continue;
8356002Sroot 
8366002Sroot 		case TS_WONT:
83727983Sminshall 			if (hisopts[c] != OPT_NO)
8386002Sroot 				wontoption(c);
8396002Sroot 			state = TS_DATA;
8406002Sroot 			continue;
8416002Sroot 
8426002Sroot 		case TS_DO:
84327983Sminshall 			if (myopts[c] != OPT_YES)
8446002Sroot 				dooption(c);
8456002Sroot 			state = TS_DATA;
8466002Sroot 			continue;
8476002Sroot 
8486002Sroot 		case TS_DONT:
84927983Sminshall 			if (myopts[c] != OPT_NO) {
85027649Sminshall 				dontoption(c);
8516002Sroot 			}
8526002Sroot 			state = TS_DATA;
8536002Sroot 			continue;
8546002Sroot 
8556002Sroot 		default:
85627898Skarels 			syslog(LOG_ERR, "telnetd: panic state=%d\n", state);
8579218Ssam 			printf("telnetd: panic state=%d\n", state);
8586002Sroot 			exit(1);
8596002Sroot 		}
8606002Sroot 	}
8616002Sroot }
8626002Sroot 
8636002Sroot willoption(option)
8646002Sroot 	int option;
8656002Sroot {
8666002Sroot 	char *fmt;
8676002Sroot 
8686002Sroot 	switch (option) {
8696002Sroot 
8706002Sroot 	case TELOPT_BINARY:
8716002Sroot 		mode(RAW, 0);
87227188Sminshall 		fmt = doopt;
87327188Sminshall 		break;
8746002Sroot 
8756002Sroot 	case TELOPT_ECHO:
87627649Sminshall 		not42 = 0;		/* looks like a 4.2 system */
87727649Sminshall 		/*
87827649Sminshall 		 * Now, in a 4.2 system, to break them out of ECHOing
87927649Sminshall 		 * (to the terminal) mode, we need to send a "WILL ECHO".
88027649Sminshall 		 * Kludge upon kludge!
88127649Sminshall 		 */
88227983Sminshall 		if (myopts[TELOPT_ECHO] == OPT_YES) {
88327649Sminshall 		    dooption(TELOPT_ECHO);
88427649Sminshall 		}
88527649Sminshall 		fmt = dont;
88627188Sminshall 		break;
8876002Sroot 
88827649Sminshall 	case TELOPT_TTYPE:
88927983Sminshall 		settimer(ttypeopt);
89027983Sminshall 		if (hisopts[TELOPT_TTYPE] == OPT_YES_BUT_ALWAYS_LOOK) {
89127983Sminshall 		    hisopts[TELOPT_TTYPE] = OPT_YES;
89227983Sminshall 		    return;
89327983Sminshall 		}
89427983Sminshall 		fmt = doopt;
89527983Sminshall 		break;
89627983Sminshall 
8976002Sroot 	case TELOPT_SGA:
8986002Sroot 		fmt = doopt;
8996002Sroot 		break;
9006002Sroot 
9016002Sroot 	case TELOPT_TM:
9026002Sroot 		fmt = dont;
9036002Sroot 		break;
9046002Sroot 
9056002Sroot 	default:
9066002Sroot 		fmt = dont;
9076002Sroot 		break;
9086002Sroot 	}
90927188Sminshall 	if (fmt == doopt) {
91027983Sminshall 		hisopts[option] = OPT_YES;
91127188Sminshall 	} else {
91227983Sminshall 		hisopts[option] = OPT_NO;
91327188Sminshall 	}
91432452Sbostic 	(void) sprintf(nfrontp, fmt, option);
9158379Ssam 	nfrontp += sizeof (dont) - 2;
9166002Sroot }
9176002Sroot 
9186002Sroot wontoption(option)
9196002Sroot 	int option;
9206002Sroot {
9216002Sroot 	char *fmt;
9226002Sroot 
9236002Sroot 	switch (option) {
9246002Sroot 	case TELOPT_ECHO:
92527649Sminshall 		not42 = 1;		/* doesn't seem to be a 4.2 system */
92627188Sminshall 		break;
9276002Sroot 
9286002Sroot 	case TELOPT_BINARY:
9296002Sroot 		mode(0, RAW);
9306002Sroot 		break;
93128044Sminshall 
93228044Sminshall 	case TELOPT_TTYPE:
93328044Sminshall 	    settimer(ttypeopt);
93428044Sminshall 	    break;
9356002Sroot 	}
93628044Sminshall 
93727188Sminshall 	fmt = dont;
93827983Sminshall 	hisopts[option] = OPT_NO;
93932452Sbostic 	(void) sprintf(nfrontp, fmt, option);
9408379Ssam 	nfrontp += sizeof (doopt) - 2;
9416002Sroot }
9426002Sroot 
9436002Sroot dooption(option)
9446002Sroot 	int option;
9456002Sroot {
9466002Sroot 	char *fmt;
9476002Sroot 
9486002Sroot 	switch (option) {
9496002Sroot 
9506002Sroot 	case TELOPT_TM:
9516002Sroot 		fmt = wont;
9526002Sroot 		break;
9536002Sroot 
9546002Sroot 	case TELOPT_ECHO:
9556002Sroot 		mode(ECHO|CRMOD, 0);
95627188Sminshall 		fmt = will;
95727188Sminshall 		break;
9586002Sroot 
9596002Sroot 	case TELOPT_BINARY:
9606002Sroot 		mode(RAW, 0);
96127188Sminshall 		fmt = will;
96227188Sminshall 		break;
9636002Sroot 
9646002Sroot 	case TELOPT_SGA:
9656002Sroot 		fmt = will;
9666002Sroot 		break;
9676002Sroot 
9686002Sroot 	default:
9696002Sroot 		fmt = wont;
9706002Sroot 		break;
9716002Sroot 	}
97227188Sminshall 	if (fmt == will) {
97327983Sminshall 	    myopts[option] = OPT_YES;
97427188Sminshall 	} else {
97527983Sminshall 	    myopts[option] = OPT_NO;
97627188Sminshall 	}
97732452Sbostic 	(void) sprintf(nfrontp, fmt, option);
9788379Ssam 	nfrontp += sizeof (doopt) - 2;
9796002Sroot }
9806002Sroot 
98127649Sminshall 
98227649Sminshall dontoption(option)
98327649Sminshall int option;
98427649Sminshall {
98527649Sminshall     char *fmt;
98627649Sminshall 
98727649Sminshall     switch (option) {
98827649Sminshall     case TELOPT_ECHO:		/* we should stop echoing */
98932401Sminshall 	mode(0, ECHO);
99027649Sminshall 	fmt = wont;
99127649Sminshall 	break;
99227983Sminshall 
99327649Sminshall     default:
99427649Sminshall 	fmt = wont;
99527649Sminshall 	break;
99627649Sminshall     }
99727983Sminshall 
99827649Sminshall     if (fmt = wont) {
99927983Sminshall 	myopts[option] = OPT_NO;
100027649Sminshall     } else {
100127983Sminshall 	myopts[option] = OPT_YES;
100227649Sminshall     }
100332452Sbostic     (void) sprintf(nfrontp, fmt, option);
100427649Sminshall     nfrontp += sizeof (wont) - 2;
100527649Sminshall }
100627649Sminshall 
100727649Sminshall /*
100827649Sminshall  * suboption()
100927649Sminshall  *
101027649Sminshall  *	Look at the sub-option buffer, and try to be helpful to the other
101127649Sminshall  * side.
101227649Sminshall  *
101327649Sminshall  *	Currently we recognize:
101427649Sminshall  *
101527983Sminshall  *	Terminal type is
101627649Sminshall  */
101727649Sminshall 
101827649Sminshall suboption()
101927649Sminshall {
102027983Sminshall     switch (SB_GET()) {
102127983Sminshall     case TELOPT_TTYPE: {		/* Yaaaay! */
102227983Sminshall 	static char terminalname[5+41] = "TERM=";
102327983Sminshall 
102427983Sminshall 	settimer(ttypesubopt);
102527983Sminshall 
102627983Sminshall 	if (SB_GET() != TELQUAL_IS) {
102727983Sminshall 	    return;		/* ??? XXX but, this is the most robust */
102827983Sminshall 	}
102927983Sminshall 
103027983Sminshall 	terminaltype = terminalname+strlen(terminalname);
103127983Sminshall 
103227983Sminshall 	while ((terminaltype < (terminalname + sizeof terminalname-1)) &&
103327983Sminshall 								    !SB_EOF()) {
103427983Sminshall 	    register int c;
103527983Sminshall 
103627983Sminshall 	    c = SB_GET();
103727983Sminshall 	    if (isupper(c)) {
103827983Sminshall 		c = tolower(c);
103927983Sminshall 	    }
104027983Sminshall 	    *terminaltype++ = c;    /* accumulate name */
104127983Sminshall 	}
104227983Sminshall 	*terminaltype = 0;
104327983Sminshall 	terminaltype = terminalname;
104427983Sminshall 	break;
104527983Sminshall     }
104627983Sminshall 
104727649Sminshall     default:
104827649Sminshall 	;
104927649Sminshall     }
105027649Sminshall }
105127649Sminshall 
10526002Sroot mode(on, off)
10536002Sroot 	int on, off;
10546002Sroot {
10556002Sroot 	struct sgttyb b;
10566002Sroot 
10576002Sroot 	ptyflush();
10586002Sroot 	ioctl(pty, TIOCGETP, &b);
10596002Sroot 	b.sg_flags |= on;
10606002Sroot 	b.sg_flags &= ~off;
10616002Sroot 	ioctl(pty, TIOCSETP, &b);
10626002Sroot }
10636002Sroot 
10646002Sroot /*
10656002Sroot  * Send interrupt to process on other side of pty.
10666002Sroot  * If it is in raw mode, just write NULL;
10676002Sroot  * otherwise, write intr char.
10686002Sroot  */
10696002Sroot interrupt()
10706002Sroot {
10716002Sroot 	struct sgttyb b;
10726002Sroot 	struct tchars tchars;
10736002Sroot 
10746002Sroot 	ptyflush();	/* half-hearted */
10756002Sroot 	ioctl(pty, TIOCGETP, &b);
10766002Sroot 	if (b.sg_flags & RAW) {
10776002Sroot 		*pfrontp++ = '\0';
10786002Sroot 		return;
10796002Sroot 	}
10806002Sroot 	*pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ?
10816002Sroot 		'\177' : tchars.t_intrc;
10826002Sroot }
10836002Sroot 
108427229Sminshall /*
108527229Sminshall  * Send quit to process on other side of pty.
108627229Sminshall  * If it is in raw mode, just write NULL;
108727229Sminshall  * otherwise, write quit char.
108827229Sminshall  */
108927229Sminshall sendbrk()
109027229Sminshall {
109127229Sminshall 	struct sgttyb b;
109227229Sminshall 	struct tchars tchars;
109327229Sminshall 
109427229Sminshall 	ptyflush();	/* half-hearted */
109527229Sminshall 	ioctl(pty, TIOCGETP, &b);
109627229Sminshall 	if (b.sg_flags & RAW) {
109727229Sminshall 		*pfrontp++ = '\0';
109827229Sminshall 		return;
109927229Sminshall 	}
110027229Sminshall 	*pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ?
110127229Sminshall 		'\034' : tchars.t_quitc;
110227229Sminshall }
110327229Sminshall 
11046002Sroot ptyflush()
11056002Sroot {
11066002Sroot 	int n;
11076002Sroot 
11086002Sroot 	if ((n = pfrontp - pbackp) > 0)
11096002Sroot 		n = write(pty, pbackp, n);
11108346Ssam 	if (n < 0)
11118346Ssam 		return;
11126002Sroot 	pbackp += n;
11136002Sroot 	if (pbackp == pfrontp)
11146002Sroot 		pbackp = pfrontp = ptyobuf;
11156002Sroot }
111627260Sminshall 
111727260Sminshall /*
111827260Sminshall  * nextitem()
111927260Sminshall  *
112027260Sminshall  *	Return the address of the next "item" in the TELNET data
112127260Sminshall  * stream.  This will be the address of the next character if
112227260Sminshall  * the current address is a user data character, or it will
112327260Sminshall  * be the address of the character following the TELNET command
112427260Sminshall  * if the current address is a TELNET IAC ("I Am a Command")
112527260Sminshall  * character.
112627260Sminshall  */
11276002Sroot 
112827260Sminshall char *
112927260Sminshall nextitem(current)
113027260Sminshall char	*current;
11316002Sroot {
113227260Sminshall     if ((*current&0xff) != IAC) {
113327260Sminshall 	return current+1;
113427260Sminshall     }
113527260Sminshall     switch (*(current+1)&0xff) {
113627260Sminshall     case DO:
113727260Sminshall     case DONT:
113827260Sminshall     case WILL:
113927260Sminshall     case WONT:
114027260Sminshall 	return current+3;
114127260Sminshall     case SB:		/* loop forever looking for the SE */
114227260Sminshall 	{
114327260Sminshall 	    register char *look = current+2;
11446002Sroot 
114527260Sminshall 	    for (;;) {
114627260Sminshall 		if ((*look++&0xff) == IAC) {
114727260Sminshall 		    if ((*look++&0xff) == SE) {
114827260Sminshall 			return look;
114927260Sminshall 		    }
115027260Sminshall 		}
115127260Sminshall 	    }
11528346Ssam 	}
115327260Sminshall     default:
115427260Sminshall 	return current+2;
115527260Sminshall     }
11566002Sroot }
11576002Sroot 
115827185Sminshall 
115927185Sminshall /*
116027260Sminshall  * netclear()
116127260Sminshall  *
116227260Sminshall  *	We are about to do a TELNET SYNCH operation.  Clear
116327260Sminshall  * the path to the network.
116427260Sminshall  *
116527260Sminshall  *	Things are a bit tricky since we may have sent the first
116627260Sminshall  * byte or so of a previous TELNET command into the network.
116727260Sminshall  * So, we have to scan the network buffer from the beginning
116827260Sminshall  * until we are up to where we want to be.
116927260Sminshall  *
117027260Sminshall  *	A side effect of what we do, just to keep things
117127260Sminshall  * simple, is to clear the urgent data pointer.  The principal
117227260Sminshall  * caller should be setting the urgent data pointer AFTER calling
117327260Sminshall  * us in any case.
117427260Sminshall  */
117527260Sminshall 
117627260Sminshall netclear()
117727260Sminshall {
117827260Sminshall     register char *thisitem, *next;
117927260Sminshall     char *good;
118027260Sminshall #define	wewant(p)	((nfrontp > p) && ((*p&0xff) == IAC) && \
118127260Sminshall 				((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
118227260Sminshall 
118327260Sminshall     thisitem = netobuf;
118427260Sminshall 
118527260Sminshall     while ((next = nextitem(thisitem)) <= nbackp) {
118627260Sminshall 	thisitem = next;
118727260Sminshall     }
118827260Sminshall 
118927260Sminshall     /* Now, thisitem is first before/at boundary. */
119027260Sminshall 
119127260Sminshall     good = netobuf;	/* where the good bytes go */
119227260Sminshall 
119327260Sminshall     while (nfrontp > thisitem) {
119427260Sminshall 	if (wewant(thisitem)) {
119527260Sminshall 	    int length;
119627260Sminshall 
119727260Sminshall 	    next = thisitem;
119827260Sminshall 	    do {
119927260Sminshall 		next = nextitem(next);
120027260Sminshall 	    } while (wewant(next) && (nfrontp > next));
120127260Sminshall 	    length = next-thisitem;
120227260Sminshall 	    bcopy(thisitem, good, length);
120327260Sminshall 	    good += length;
120427260Sminshall 	    thisitem = next;
120527260Sminshall 	} else {
120627260Sminshall 	    thisitem = nextitem(thisitem);
120727260Sminshall 	}
120827260Sminshall     }
120927260Sminshall 
121027260Sminshall     nbackp = netobuf;
121127260Sminshall     nfrontp = good;		/* next byte to be sent */
121227260Sminshall     neturg = 0;
121327260Sminshall }
121427260Sminshall 
121527260Sminshall /*
121627185Sminshall  *  netflush
121727185Sminshall  *		Send as much data as possible to the network,
121827185Sminshall  *	handling requests for urgent data.
121927185Sminshall  */
122027185Sminshall 
122127185Sminshall 
122227185Sminshall netflush()
122327185Sminshall {
122427185Sminshall     int n;
122527185Sminshall 
122627185Sminshall     if ((n = nfrontp - nbackp) > 0) {
122727649Sminshall 	/*
122827649Sminshall 	 * if no urgent data, or if the other side appears to be an
122927649Sminshall 	 * old 4.2 client (and thus unable to survive TCP urgent data),
123027649Sminshall 	 * write the entire buffer in non-OOB mode.
123127649Sminshall 	 */
123227649Sminshall 	if ((neturg == 0) || (not42 == 0)) {
123327185Sminshall 	    n = write(net, nbackp, n);	/* normal write */
123427185Sminshall 	} else {
123527185Sminshall 	    n = neturg - nbackp;
123627185Sminshall 	    /*
123727185Sminshall 	     * In 4.2 (and 4.3) systems, there is some question about
123827185Sminshall 	     * what byte in a sendOOB operation is the "OOB" data.
123927185Sminshall 	     * To make ourselves compatible, we only send ONE byte
124027185Sminshall 	     * out of band, the one WE THINK should be OOB (though
124127185Sminshall 	     * we really have more the TCP philosophy of urgent data
124227185Sminshall 	     * rather than the Unix philosophy of OOB data).
124327185Sminshall 	     */
124427185Sminshall 	    if (n > 1) {
124527185Sminshall 		n = send(net, nbackp, n-1, 0);	/* send URGENT all by itself */
124627185Sminshall 	    } else {
124727185Sminshall 		n = send(net, nbackp, n, MSG_OOB);	/* URGENT data */
124827185Sminshall 	    }
124927185Sminshall 	}
125027185Sminshall     }
125127185Sminshall     if (n < 0) {
125227185Sminshall 	if (errno == EWOULDBLOCK)
125327185Sminshall 	    return;
125427185Sminshall 	/* should blow this guy away... */
125527185Sminshall 	return;
125627185Sminshall     }
125727185Sminshall     nbackp += n;
125827185Sminshall     if (nbackp >= neturg) {
125927185Sminshall 	neturg = 0;
126027185Sminshall     }
126127185Sminshall     if (nbackp == nfrontp) {
126227185Sminshall 	nbackp = nfrontp = netobuf;
126327185Sminshall     }
126427185Sminshall }
126527185Sminshall 
12666002Sroot cleanup()
12676002Sroot {
12686002Sroot 
12696002Sroot 	rmut();
127010191Ssam 	shutdown(net, 2);
12716002Sroot 	exit(1);
12726002Sroot }
12736002Sroot 
12746002Sroot #include <utmp.h>
12756002Sroot 
12766002Sroot struct	utmp wtmp;
12776002Sroot char	wtmpf[]	= "/usr/adm/wtmp";
127823567Sbloom char	utmpf[] = "/etc/utmp";
127923567Sbloom #define SCPYN(a, b)	strncpy(a, b, sizeof(a))
128023567Sbloom #define SCMPN(a, b)	strncmp(a, b, sizeof(a))
12816002Sroot 
12826002Sroot rmut()
12836002Sroot {
12846002Sroot 	register f;
12856002Sroot 	int found = 0;
128623567Sbloom 	struct utmp *u, *utmp;
128723567Sbloom 	int nutmp;
128823567Sbloom 	struct stat statbf;
12896002Sroot 
129023567Sbloom 	f = open(utmpf, O_RDWR);
12916002Sroot 	if (f >= 0) {
129223567Sbloom 		fstat(f, &statbf);
129323567Sbloom 		utmp = (struct utmp *)malloc(statbf.st_size);
129423567Sbloom 		if (!utmp)
129523567Sbloom 			syslog(LOG_ERR, "utmp malloc failed");
129623567Sbloom 		if (statbf.st_size && utmp) {
129723567Sbloom 			nutmp = read(f, utmp, statbf.st_size);
129823567Sbloom 			nutmp /= sizeof(struct utmp);
129923567Sbloom 
130023567Sbloom 			for (u = utmp ; u < &utmp[nutmp] ; u++) {
130123567Sbloom 				if (SCMPN(u->ut_line, line+5) ||
130223567Sbloom 				    u->ut_name[0]==0)
130323567Sbloom 					continue;
130423567Sbloom 				lseek(f, ((long)u)-((long)utmp), L_SET);
130523567Sbloom 				SCPYN(u->ut_name, "");
130623567Sbloom 				SCPYN(u->ut_host, "");
130723567Sbloom 				time(&u->ut_time);
130823567Sbloom 				write(f, (char *)u, sizeof(wtmp));
130923567Sbloom 				found++;
131023567Sbloom 			}
13116002Sroot 		}
13126002Sroot 		close(f);
13136002Sroot 	}
13146002Sroot 	if (found) {
131517583Ssam 		f = open(wtmpf, O_WRONLY|O_APPEND);
13166002Sroot 		if (f >= 0) {
13176002Sroot 			SCPYN(wtmp.ut_line, line+5);
13186002Sroot 			SCPYN(wtmp.ut_name, "");
131912683Ssam 			SCPYN(wtmp.ut_host, "");
13206002Sroot 			time(&wtmp.ut_time);
132123567Sbloom 			write(f, (char *)&wtmp, sizeof(wtmp));
13226002Sroot 			close(f);
13236002Sroot 		}
13246002Sroot 	}
13256002Sroot 	chmod(line, 0666);
13266002Sroot 	chown(line, 0, 0);
13276002Sroot 	line[strlen("/dev/")] = 'p';
13286002Sroot 	chmod(line, 0666);
13296002Sroot 	chown(line, 0, 0);
13306002Sroot }
133133271Sminshall 
133233271Sminshall char	editedhost[32];
133333271Sminshall 
133433271Sminshall edithost(pat, host)
133533271Sminshall 	register char *pat;
133633271Sminshall 	register char *host;
133733271Sminshall {
133833271Sminshall 	register char *res = editedhost;
133933271Sminshall 
134033271Sminshall 	if (!pat)
134133271Sminshall 		pat = "";
134233271Sminshall 	while (*pat) {
134333271Sminshall 		switch (*pat) {
134433271Sminshall 
134533271Sminshall 		case '#':
134633271Sminshall 			if (*host)
134733271Sminshall 				host++;
134833271Sminshall 			break;
134933271Sminshall 
135033271Sminshall 		case '@':
135133271Sminshall 			if (*host)
135233271Sminshall 				*res++ = *host++;
135333271Sminshall 			break;
135433271Sminshall 
135533271Sminshall 		default:
135633271Sminshall 			*res++ = *pat;
135733271Sminshall 			break;
135833271Sminshall 
135933271Sminshall 		}
136033271Sminshall 		if (res == &editedhost[sizeof editedhost - 1]) {
136133271Sminshall 			*res = '\0';
136233271Sminshall 			return;
136333271Sminshall 		}
136433271Sminshall 		pat++;
136533271Sminshall 	}
136633271Sminshall 	if (*host)
136733271Sminshall 		strncpy(res, host, sizeof editedhost - (res - editedhost) - 1);
136833271Sminshall 	else
136933271Sminshall 		*res = '\0';
137033271Sminshall 	editedhost[sizeof editedhost - 1] = '\0';
137133271Sminshall }
137233271Sminshall 
137333271Sminshall static char *putlocation;
137433271Sminshall 
137533271Sminshall puts(s)
137633271Sminshall register char *s;
137733271Sminshall {
137833271Sminshall 
137933271Sminshall 	while (*s)
138033271Sminshall 		putchr(*s++);
138133271Sminshall }
138233271Sminshall 
138333271Sminshall putchr(cc)
138433271Sminshall {
138533271Sminshall 	*putlocation++ = cc;
138633271Sminshall }
138733271Sminshall 
138834424Sbostic putf(cp, where)
138933271Sminshall register char *cp;
139033271Sminshall char *where;
139133271Sminshall {
139233271Sminshall 	char *slash;
139333271Sminshall 	char datebuffer[60];
139433271Sminshall 	extern char *rindex();
139533271Sminshall 
139633271Sminshall 	putlocation = where;
139733271Sminshall 
139833271Sminshall 	while (*cp) {
139933271Sminshall 		if (*cp != '%') {
140033271Sminshall 			putchr(*cp++);
140133271Sminshall 			continue;
140233271Sminshall 		}
140333271Sminshall 		switch (*++cp) {
140433271Sminshall 
140533271Sminshall 		case 't':
140633271Sminshall 			slash = rindex(line, '/');
140733271Sminshall 			if (slash == (char *) 0)
140833271Sminshall 				puts(line);
140933271Sminshall 			else
141033271Sminshall 				puts(&slash[1]);
141133271Sminshall 			break;
141233271Sminshall 
141333271Sminshall 		case 'h':
141433271Sminshall 			puts(editedhost);
141533271Sminshall 			break;
141633271Sminshall 
141733271Sminshall 		case 'd':
141833271Sminshall 			get_date(datebuffer);
141933271Sminshall 			puts(datebuffer);
142033271Sminshall 			break;
142133271Sminshall 
142233271Sminshall 		case '%':
142333271Sminshall 			putchr('%');
142433271Sminshall 			break;
142533271Sminshall 		}
142633271Sminshall 		cp++;
142733271Sminshall 	}
142833271Sminshall }
1429