xref: /csrg-svn/libexec/telnetd/telnetd.c (revision 33271)
121182Sdist /*
227898Skarels  * Copyright (c) 1983,1986 Regents of the University of California.
321182Sdist  * All rights reserved.  The Berkeley software License Agreement
421182Sdist  * specifies the terms and conditions for redistribution.
521182Sdist  */
621182Sdist 
76295Sroot #ifndef lint
821182Sdist char copyright[] =
921182Sdist "@(#) Copyright (c) 1983 Regents of the University of California.\n\
1021182Sdist  All rights reserved.\n";
1121182Sdist #endif not lint
126295Sroot 
1321182Sdist #ifndef lint
14*33271Sminshall static char sccsid[] = "@(#)telnetd.c	5.25 (Berkeley) 01/05/88";
1521182Sdist #endif not lint
1621182Sdist 
176002Sroot /*
1827898Skarels  * Telnet server.
196002Sroot  */
2027898Skarels #include <sys/param.h>
219218Ssam #include <sys/socket.h>
2213608Ssam #include <sys/wait.h>
2317583Ssam #include <sys/file.h>
2420188Skarels #include <sys/stat.h>
2527185Sminshall #include <sys/time.h>
269218Ssam 
279218Ssam #include <netinet/in.h>
289218Ssam 
2912216Ssam #include <arpa/telnet.h>
3012216Ssam 
316002Sroot #include <stdio.h>
326002Sroot #include <signal.h>
336002Sroot #include <errno.h>
346002Sroot #include <sgtty.h>
358346Ssam #include <netdb.h>
3617187Sralph #include <syslog.h>
3727649Sminshall #include <ctype.h>
389218Ssam 
3927983Sminshall #define	OPT_NO			0		/* won't do this option */
4027983Sminshall #define	OPT_YES			1		/* will do this option */
4127983Sminshall #define	OPT_YES_BUT_ALWAYS_LOOK	2
4227983Sminshall #define	OPT_NO_BUT_ALWAYS_LOOK	3
436002Sroot char	hisopts[256];
446002Sroot char	myopts[256];
456002Sroot 
466002Sroot char	doopt[] = { IAC, DO, '%', 'c', 0 };
476002Sroot char	dont[] = { IAC, DONT, '%', 'c', 0 };
486002Sroot char	will[] = { IAC, WILL, '%', 'c', 0 };
496002Sroot char	wont[] = { IAC, WONT, '%', 'c', 0 };
506002Sroot 
516002Sroot /*
526002Sroot  * I/O data buffers, pointers, and counters.
536002Sroot  */
546002Sroot char	ptyibuf[BUFSIZ], *ptyip = ptyibuf;
5527649Sminshall 
566002Sroot char	ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf;
5727649Sminshall 
586002Sroot char	netibuf[BUFSIZ], *netip = netibuf;
5927649Sminshall #define	NIACCUM(c)	{   *netip++ = c; \
6027649Sminshall 			    ncc++; \
6127649Sminshall 			}
6227649Sminshall 
636388Ssam char	netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf;
6427185Sminshall char	*neturg = 0;		/* one past last bye of urgent data */
6527649Sminshall 	/* the remote system seems to NOT be an old 4.2 */
6627649Sminshall int	not42 = 1;
6727649Sminshall 
68*33271Sminshall #define	BANNER	"\r\n\r\n4.3 BSD UNIX (%s)\r\n\r\r\n\r"
6927649Sminshall 
7027983Sminshall 		/* buffer for sub-options */
7127983Sminshall char	subbuffer[100], *subpointer= subbuffer, *subend= subbuffer;
7227649Sminshall #define	SB_CLEAR()	subpointer = subbuffer;
7327983Sminshall #define	SB_TERM()	{ subend = subpointer; SB_CLEAR(); }
7427649Sminshall #define	SB_ACCUM(c)	if (subpointer < (subbuffer+sizeof subbuffer)) { \
7527649Sminshall 				*subpointer++ = (c); \
7627649Sminshall 			}
7727983Sminshall #define	SB_GET()	((*subpointer++)&0xff)
7827983Sminshall #define	SB_EOF()	(subpointer >= subend)
7927649Sminshall 
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 */
9927983Sminshall 	ttypeopt,		/* ttype will/won't received */
10027983Sminshall 	ttypesubopt,		/* ttype subopt is received */
10127983Sminshall 	getterminal,		/* time started to get terminal information */
10227185Sminshall 	gotDM;			/* when did we last see a data mark */
10327185Sminshall } clocks;
10427185Sminshall 
10527983Sminshall #define	settimer(x)	(clocks.x = ++clocks.system)
10627983Sminshall #define	sequenceIs(x,y)	(clocks.x < clocks.y)
10727185Sminshall 
1086002Sroot main(argc, argv)
1096002Sroot 	char *argv[];
1106002Sroot {
11116371Skarels 	struct sockaddr_in from;
11217156Ssam 	int on = 1, fromlen;
1136002Sroot 
11427185Sminshall #if	defined(DEBUG)
11527185Sminshall 	{
11627185Sminshall 	    int s, ns, foo;
11727185Sminshall 	    struct servent *sp;
11827185Sminshall 	    static struct sockaddr_in sin = { AF_INET };
11927185Sminshall 
12027185Sminshall 	    sp = getservbyname("telnet", "tcp");
12127185Sminshall 	    if (sp == 0) {
12227185Sminshall 		    fprintf(stderr, "telnetd: tcp/telnet: unknown service\n");
12327185Sminshall 		    exit(1);
12427185Sminshall 	    }
12527185Sminshall 	    sin.sin_port = sp->s_port;
12627185Sminshall 	    argc--, argv++;
12727185Sminshall 	    if (argc > 0) {
12827185Sminshall 		    sin.sin_port = atoi(*argv);
12927185Sminshall 		    sin.sin_port = htons((u_short)sin.sin_port);
13027185Sminshall 	    }
13127185Sminshall 
13227185Sminshall 	    s = socket(AF_INET, SOCK_STREAM, 0);
13327185Sminshall 	    if (s < 0) {
13427185Sminshall 		    perror("telnetd: socket");;
13527185Sminshall 		    exit(1);
13627185Sminshall 	    }
13727185Sminshall 	    if (bind(s, &sin, sizeof sin) < 0) {
13827185Sminshall 		perror("bind");
13927185Sminshall 		exit(1);
14027185Sminshall 	    }
14127185Sminshall 	    if (listen(s, 1) < 0) {
14227185Sminshall 		perror("listen");
14327185Sminshall 		exit(1);
14427185Sminshall 	    }
14527185Sminshall 	    foo = sizeof sin;
14627185Sminshall 	    ns = accept(s, &sin, &foo);
14727185Sminshall 	    if (ns < 0) {
14827185Sminshall 		perror("accept");
14927185Sminshall 		exit(1);
15027185Sminshall 	    }
15127185Sminshall 	    dup2(ns, 0);
15227185Sminshall 	    close(s);
15327185Sminshall 	}
15427185Sminshall #endif	/* defined(DEBUG) */
15524855Seric 	openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON);
15616371Skarels 	fromlen = sizeof (from);
15716371Skarels 	if (getpeername(0, &from, &fromlen) < 0) {
15816371Skarels 		fprintf(stderr, "%s: ", argv[0]);
15916371Skarels 		perror("getpeername");
16016371Skarels 		_exit(1);
1618346Ssam 	}
16217156Ssam 	if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) {
16317187Sralph 		syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
16410418Ssam 	}
16516371Skarels 	doit(0, &from);
1666002Sroot }
1676002Sroot 
16827983Sminshall char	*terminaltype = 0;
16927983Sminshall char	*envinit[2];
17027983Sminshall int	cleanup();
17127649Sminshall 
17227649Sminshall /*
17327983Sminshall  * ttloop
17427649Sminshall  *
17527983Sminshall  *	A small subroutine to flush the network output buffer, get some data
17627983Sminshall  * from the network, and pass it through the telnet state machine.  We
17727983Sminshall  * also flush the pty input buffer (by dropping its data) if it becomes
17827983Sminshall  * too full.
17927983Sminshall  */
18027983Sminshall 
18127983Sminshall void
18227983Sminshall ttloop()
18327983Sminshall {
18427983Sminshall     if (nfrontp-nbackp) {
18527983Sminshall 	netflush();
18627983Sminshall     }
18727983Sminshall     ncc = read(net, netibuf, sizeof netibuf);
18827983Sminshall     if (ncc < 0) {
18927983Sminshall 	syslog(LOG_INFO, "ttloop:  read: %m\n");
19028044Sminshall 	exit(1);
19128044Sminshall     } else if (ncc == 0) {
19228044Sminshall 	syslog(LOG_INFO, "ttloop:  peer died: %m\n");
19328044Sminshall 	exit(1);
19427983Sminshall     }
19527983Sminshall     netip = netibuf;
19627983Sminshall     telrcv();			/* state machine */
19727983Sminshall     if (ncc > 0) {
19827983Sminshall 	pfrontp = pbackp = ptyobuf;
19927983Sminshall 	telrcv();
20027983Sminshall     }
20127983Sminshall }
20227983Sminshall 
20327983Sminshall /*
20427983Sminshall  * getterminaltype
20527649Sminshall  *
20627983Sminshall  *	Ask the other end to send along its terminal type.
20727983Sminshall  * Output is the variable terminaltype filled in.
20827649Sminshall  */
20927649Sminshall 
21027983Sminshall void
21127983Sminshall getterminaltype()
21227649Sminshall {
21327983Sminshall     static char sbuf[] = { IAC, DO, TELOPT_TTYPE };
21427649Sminshall 
21527983Sminshall     settimer(getterminal);
21627983Sminshall     bcopy(sbuf, nfrontp, sizeof sbuf);
21727983Sminshall     nfrontp += sizeof sbuf;
21828044Sminshall     hisopts[TELOPT_TTYPE] = OPT_YES_BUT_ALWAYS_LOOK;
21927983Sminshall     while (sequenceIs(ttypeopt, getterminal)) {
22027983Sminshall 	ttloop();
22127649Sminshall     }
22227983Sminshall     if (hisopts[TELOPT_TTYPE] == OPT_YES) {
22327983Sminshall 	static char sbbuf[] = { IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE };
22427983Sminshall 
22527983Sminshall 	bcopy(sbbuf, nfrontp, sizeof sbbuf);
22627983Sminshall 	nfrontp += sizeof sbbuf;
22727983Sminshall 	while (sequenceIs(ttypesubopt, getterminal)) {
22827983Sminshall 	    ttloop();
22927983Sminshall 	}
23027983Sminshall     }
23127649Sminshall }
23227649Sminshall 
2336002Sroot /*
2346002Sroot  * Get a pty, scan input lines.
2356002Sroot  */
23612683Ssam doit(f, who)
23712683Ssam 	int f;
23812683Ssam 	struct sockaddr_in *who;
2396002Sroot {
24020188Skarels 	char *host, *inet_ntoa();
24117583Ssam 	int i, p, t;
2426002Sroot 	struct sgttyb b;
24312683Ssam 	struct hostent *hp;
24427649Sminshall 	int c;
2456002Sroot 
24620188Skarels 	for (c = 'p'; c <= 's'; c++) {
24720188Skarels 		struct stat stb;
24820188Skarels 
24920188Skarels 		line = "/dev/ptyXX";
25020188Skarels 		line[strlen("/dev/pty")] = c;
25120188Skarels 		line[strlen("/dev/ptyp")] = '0';
25220188Skarels 		if (stat(line, &stb) < 0)
25320188Skarels 			break;
25417583Ssam 		for (i = 0; i < 16; i++) {
25520188Skarels 			line[strlen("/dev/ptyp")] = "0123456789abcdef"[i];
25620188Skarels 			p = open(line, 2);
25717583Ssam 			if (p > 0)
25817583Ssam 				goto gotpty;
25917583Ssam 		}
2606002Sroot 	}
2619244Ssam 	fatal(f, "All network ports in use");
2629244Ssam 	/*NOTREACHED*/
2636002Sroot gotpty:
2646002Sroot 	dup2(f, 0);
26520188Skarels 	line[strlen("/dev/")] = 't';
26617583Ssam 	t = open("/dev/tty", O_RDWR);
2676002Sroot 	if (t >= 0) {
2686002Sroot 		ioctl(t, TIOCNOTTY, 0);
2696002Sroot 		close(t);
2706002Sroot 	}
27120188Skarels 	t = open(line, O_RDWR);
2729244Ssam 	if (t < 0)
27320188Skarels 		fatalperror(f, line, errno);
2746002Sroot 	ioctl(t, TIOCGETP, &b);
2756388Ssam 	b.sg_flags = CRMOD|XTABS|ANYP;
2766002Sroot 	ioctl(t, TIOCSETP, &b);
2776388Ssam 	ioctl(p, TIOCGETP, &b);
2788379Ssam 	b.sg_flags &= ~ECHO;
2796388Ssam 	ioctl(p, TIOCSETP, &b);
28012683Ssam 	hp = gethostbyaddr(&who->sin_addr, sizeof (struct in_addr),
28112683Ssam 		who->sin_family);
28212683Ssam 	if (hp)
28312683Ssam 		host = hp->h_name;
28412683Ssam 	else
28517444Sralph 		host = inet_ntoa(who->sin_addr);
28627983Sminshall 
28727983Sminshall 	net = f;
28827983Sminshall 	pty = p;
28927983Sminshall 
29027983Sminshall 	/*
29127983Sminshall 	 * get terminal type.
29227983Sminshall 	 */
29327983Sminshall 	getterminaltype();
29427983Sminshall 
2959244Ssam 	if ((i = fork()) < 0)
2969244Ssam 		fatalperror(f, "fork", errno);
2976002Sroot 	if (i)
2986002Sroot 		telnet(f, p);
2996002Sroot 	close(f);
3006002Sroot 	close(p);
3016002Sroot 	dup2(t, 0);
3026002Sroot 	dup2(t, 1);
3036002Sroot 	dup2(t, 2);
3046002Sroot 	close(t);
30527983Sminshall 	envinit[0] = terminaltype;
30627983Sminshall 	envinit[1] = 0;
30713799Ssam 	environ = envinit;
30827649Sminshall 	/*
30927649Sminshall 	 * -h : pass on name of host.
31027983Sminshall 	 *		WARNING:  -h is accepted by login if and only if
31127983Sminshall 	 *			getuid() == 0.
31227649Sminshall 	 * -p : don't clobber the environment (so terminal type stays set).
31327649Sminshall 	 */
31427649Sminshall 	execl("/bin/login", "login", "-h", host,
31527983Sminshall 					terminaltype ? "-p" : 0, 0);
3169244Ssam 	fatalperror(f, "/bin/login", errno);
3179244Ssam 	/*NOTREACHED*/
3189244Ssam }
3199244Ssam 
3209244Ssam fatal(f, msg)
3219244Ssam 	int f;
3229244Ssam 	char *msg;
3239244Ssam {
3249244Ssam 	char buf[BUFSIZ];
3259244Ssam 
32617583Ssam 	(void) sprintf(buf, "telnetd: %s.\r\n", msg);
3279244Ssam 	(void) write(f, buf, strlen(buf));
3286002Sroot 	exit(1);
3296002Sroot }
3306002Sroot 
3319244Ssam fatalperror(f, msg, errno)
3329244Ssam 	int f;
3339244Ssam 	char *msg;
3349244Ssam 	int errno;
3359244Ssam {
3369244Ssam 	char buf[BUFSIZ];
3379244Ssam 	extern char *sys_errlist[];
3389244Ssam 
33917583Ssam 	(void) sprintf(buf, "%s: %s\r\n", msg, sys_errlist[errno]);
3409244Ssam 	fatal(f, buf);
3419244Ssam }
3429244Ssam 
34327185Sminshall 
3446002Sroot /*
34527185Sminshall  * Check a descriptor to see if out of band data exists on it.
34627185Sminshall  */
34727185Sminshall 
34827185Sminshall 
34927185Sminshall stilloob(s)
35027185Sminshall int	s;		/* socket number */
35127185Sminshall {
35227185Sminshall     static struct timeval timeout = { 0 };
35327185Sminshall     fd_set	excepts;
35427185Sminshall     int value;
35527185Sminshall 
35627185Sminshall     do {
35727185Sminshall 	FD_ZERO(&excepts);
35827185Sminshall 	FD_SET(s, &excepts);
35927185Sminshall 	value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout);
36027898Skarels     } while ((value == -1) && (errno == EINTR));
36127185Sminshall 
36227185Sminshall     if (value < 0) {
36327185Sminshall 	fatalperror(pty, "select", errno);
36427185Sminshall     }
36527185Sminshall     if (FD_ISSET(s, &excepts)) {
36627185Sminshall 	return 1;
36727185Sminshall     } else {
36827185Sminshall 	return 0;
36927185Sminshall     }
37027185Sminshall }
37127185Sminshall 
37227185Sminshall /*
3736002Sroot  * Main loop.  Select from pty and network, and
3746002Sroot  * hand data to telnet receiver finite state machine.
3756002Sroot  */
3766002Sroot telnet(f, p)
3776002Sroot {
3786002Sroot 	int on = 1;
37927898Skarels 	char hostname[MAXHOSTNAMELEN];
380*33271Sminshall #define	TABBUFSIZ	512
381*33271Sminshall 	char	defent[TABBUFSIZ];
382*33271Sminshall 	char	defstrs[TABBUFSIZ];
383*33271Sminshall #undef	TABBUFSIZ
384*33271Sminshall 	char *HE;
385*33271Sminshall 	char *HN;
386*33271Sminshall 	char *IM;
3876002Sroot 
3886002Sroot 	ioctl(f, FIONBIO, &on);
3896002Sroot 	ioctl(p, FIONBIO, &on);
39033267Sminshall 	ioctl(p, TIOCPKT, &on);
39127649Sminshall #if	defined(SO_OOBINLINE)
39227649Sminshall 	setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on);
39327649Sminshall #endif	/* defined(SO_OOBINLINE) */
3946002Sroot 	signal(SIGTSTP, SIG_IGN);
39532400Sminshall 	/*
39632400Sminshall 	 * Ignoring SIGTTOU keeps the kernel from blocking us
39732400Sminshall 	 * in ttioctl() in /sys/tty.c.
39832400Sminshall 	 */
39932400Sminshall 	signal(SIGTTOU, SIG_IGN);
40013028Ssam 	signal(SIGCHLD, cleanup);
40126083Slepreau 	setpgrp(0, 0);
4026002Sroot 
4038379Ssam 	/*
40427185Sminshall 	 * Request to do remote echo and to suppress go ahead.
4058379Ssam 	 */
40627983Sminshall 	if (!myopts[TELOPT_ECHO]) {
40727983Sminshall 	    dooption(TELOPT_ECHO);
40827983Sminshall 	}
40927983Sminshall 	if (!myopts[TELOPT_SGA]) {
41027983Sminshall 	    dooption(TELOPT_SGA);
41127983Sminshall 	}
41212713Ssam 	/*
41327649Sminshall 	 * Is the client side a 4.2 (NOT 4.3) system?  We need to know this
41427649Sminshall 	 * because 4.2 clients are unable to deal with TCP urgent data.
41527649Sminshall 	 *
41627649Sminshall 	 * To find out, we send out a "DO ECHO".  If the remote system
41727649Sminshall 	 * answers "WILL ECHO" it is probably a 4.2 client, and we note
41827649Sminshall 	 * that fact ("WILL ECHO" ==> that the client will echo what
41927649Sminshall 	 * WE, the server, sends it; it does NOT mean that the client will
42027649Sminshall 	 * echo the terminal input).
42127649Sminshall 	 */
42232452Sbostic 	(void) sprintf(nfrontp, doopt, TELOPT_ECHO);
42327649Sminshall 	nfrontp += sizeof doopt-2;
42427983Sminshall 	hisopts[TELOPT_ECHO] = OPT_YES_BUT_ALWAYS_LOOK;
42527649Sminshall 
42627649Sminshall 	/*
42712713Ssam 	 * Show banner that getty never gave.
42827797Sminshall 	 *
429*33271Sminshall 	 * We put the banner in the pty input buffer.  This way, it
430*33271Sminshall 	 * gets carriage return null processing, etc., just like all
431*33271Sminshall 	 * other pty --> client data.
43212713Ssam 	 */
43327797Sminshall 
43412713Ssam 	gethostname(hostname, sizeof (hostname));
435*33271Sminshall 	if (getent(defent, "default") == 1) {
436*33271Sminshall 		char *getstr();
437*33271Sminshall 		char *p=defstrs;
43827649Sminshall 
439*33271Sminshall 		HE = getstr("he", &p);
440*33271Sminshall 		HN = getstr("hn", &p);
441*33271Sminshall 		IM = getstr("im", &p);
442*33271Sminshall 		if (HN && *HN)
443*33271Sminshall 			strcpy(hostname, HN);
444*33271Sminshall 		edithost(HE, hostname);
445*33271Sminshall 		if (IM && *IM)
446*33271Sminshall 			putf(IM, ptyibuf+1, p);
447*33271Sminshall 	} else {
448*33271Sminshall 		sprintf(ptyibuf+1, BANNER, hostname);
449*33271Sminshall 	}
45027797Sminshall 
451*33271Sminshall 	ptyip = ptyibuf+1;		/* Prime the pump */
452*33271Sminshall 	pcc = strlen(ptyip);		/* ditto */
453*33271Sminshall 
45433267Sminshall 	/* Clear ptybuf[0] - where the packet information is received */
45533267Sminshall 	ptyibuf[0] = 0;
456*33271Sminshall 
45727649Sminshall 	/*
45827649Sminshall 	 * Call telrcv() once to pick up anything received during
45927649Sminshall 	 * terminal type negotiation.
46027649Sminshall 	 */
46127649Sminshall 	telrcv();
46227649Sminshall 
4636002Sroot 	for (;;) {
46427185Sminshall 		fd_set ibits, obits, xbits;
4656002Sroot 		register int c;
4666002Sroot 
46727185Sminshall 		if (ncc < 0 && pcc < 0)
46827185Sminshall 			break;
46927185Sminshall 
47027185Sminshall 		FD_ZERO(&ibits);
47127185Sminshall 		FD_ZERO(&obits);
47227185Sminshall 		FD_ZERO(&xbits);
4736002Sroot 		/*
4746002Sroot 		 * Never look for input if there's still
4756002Sroot 		 * stuff in the corresponding output buffer
4766002Sroot 		 */
47727185Sminshall 		if (nfrontp - nbackp || pcc > 0) {
47827185Sminshall 			FD_SET(f, &obits);
47933267Sminshall 			FD_SET(p, &xbits);
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)) {
51327649Sminshall #if	!defined(SO_OOBINLINE)
51427185Sminshall 			/*
51527898Skarels 			 * In 4.2 (and 4.3 beta) 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));
55627983Sminshall 				if (sequenceIs(didnetreceive, 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);
56727649Sminshall #else	/* !defined(SO_OOBINLINE)) */
56827185Sminshall 		    ncc = read(net, netibuf, sizeof (netibuf));
56927649Sminshall #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 		 */
58333267Sminshall 		if (FD_ISSET(p, &xbits)) {
58433267Sminshall 			if (read(p, ptyibuf, 1) != 1) {
58533267Sminshall 				break;
58633267Sminshall 			}
58733267Sminshall 		}
58827185Sminshall 		if (FD_ISSET(p, &ibits)) {
5896002Sroot 			pcc = read(p, ptyibuf, BUFSIZ);
5906002Sroot 			if (pcc < 0 && errno == EWOULDBLOCK)
5916002Sroot 				pcc = 0;
5926002Sroot 			else {
5936002Sroot 				if (pcc <= 0)
5946002Sroot 					break;
59533267Sminshall 				/* Skip past "packet" */
59633267Sminshall 				pcc--;
59733267Sminshall 				ptyip = ptyibuf+1;
5986002Sroot 			}
5996002Sroot 		}
60033267Sminshall 		if (ptyibuf[0] & TIOCPKT_FLUSHWRITE) {
60133267Sminshall 			netclear();	/* clear buffer back */
60233267Sminshall 			*nfrontp++ = IAC;
60333267Sminshall 			*nfrontp++ = DM;
60433267Sminshall 			neturg = nfrontp-1;  /* off by one XXX */
60533267Sminshall 			ptyibuf[0] = 0;
60633267Sminshall 		}
6076002Sroot 
6086002Sroot 		while (pcc > 0) {
6096002Sroot 			if ((&netobuf[BUFSIZ] - nfrontp) < 2)
6106002Sroot 				break;
6116002Sroot 			c = *ptyip++ & 0377, pcc--;
6126002Sroot 			if (c == IAC)
6136002Sroot 				*nfrontp++ = c;
6146002Sroot 			*nfrontp++ = c;
61531940Sbostic 			/* Don't do CR-NUL if we are in binary mode */
61631940Sbostic 			if ((c == '\r') && (myopts[TELOPT_BINARY] == OPT_NO)) {
61727020Sminshall 				if (pcc > 0 && ((*ptyip & 0377) == '\n')) {
61827020Sminshall 					*nfrontp++ = *ptyip++ & 0377;
61927020Sminshall 					pcc--;
62027020Sminshall 				} else
62127020Sminshall 					*nfrontp++ = '\0';
62227020Sminshall 			}
6236002Sroot 		}
62427185Sminshall 		if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0)
6256002Sroot 			netflush();
6266002Sroot 		if (ncc > 0)
6276002Sroot 			telrcv();
62827185Sminshall 		if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0)
6296002Sroot 			ptyflush();
6306002Sroot 	}
6316002Sroot 	cleanup();
6326002Sroot }
6336002Sroot 
6346002Sroot /*
6356002Sroot  * State for recv fsm
6366002Sroot  */
6376002Sroot #define	TS_DATA		0	/* base state */
6386002Sroot #define	TS_IAC		1	/* look for double IAC's */
6396002Sroot #define	TS_CR		2	/* CR-LF ->'s CR */
64027649Sminshall #define	TS_SB		3	/* throw away begin's... */
64127649Sminshall #define	TS_SE		4	/* ...end's (suboption negotiation) */
6426002Sroot #define	TS_WILL		5	/* will option negotiation */
6436002Sroot #define	TS_WONT		6	/* wont " */
6446002Sroot #define	TS_DO		7	/* do " */
6456002Sroot #define	TS_DONT		8	/* dont " */
6466002Sroot 
6476002Sroot telrcv()
6486002Sroot {
6496002Sroot 	register int c;
6506002Sroot 	static int state = TS_DATA;
6516002Sroot 
6526002Sroot 	while (ncc > 0) {
6536002Sroot 		if ((&ptyobuf[BUFSIZ] - pfrontp) < 2)
6546002Sroot 			return;
6556002Sroot 		c = *netip++ & 0377, ncc--;
6566002Sroot 		switch (state) {
6576002Sroot 
65826090Sminshall 		case TS_CR:
65926090Sminshall 			state = TS_DATA;
66032097Sminshall 			/* Strip off \n or \0 after a \r */
66126499Sminshall 			if ((c == 0) || (c == '\n')) {
66226090Sminshall 				break;
66326499Sminshall 			}
66426090Sminshall 			/* FALL THROUGH */
66526090Sminshall 
6666002Sroot 		case TS_DATA:
6676002Sroot 			if (c == IAC) {
6686002Sroot 				state = TS_IAC;
6696002Sroot 				break;
6706002Sroot 			}
6716002Sroot 			if (inter > 0)
6726002Sroot 				break;
67327020Sminshall 			/*
67432097Sminshall 			 * We now map \r\n ==> \r for pragmatic reasons.
67532097Sminshall 			 * Many client implementations send \r\n when
67632097Sminshall 			 * the user hits the CarriageReturn key.
67732097Sminshall 			 *
67832097Sminshall 			 * We USED to map \r\n ==> \n, since \r\n says
67927020Sminshall 			 * that we want to be in column 1 of the next
68027020Sminshall 			 * printable line, and \n is the standard
68127020Sminshall 			 * unix way of saying that (\r is only good
68227020Sminshall 			 * if CRMOD is set, which it normally is).
68327020Sminshall 			 */
68431940Sbostic 			if ((c == '\r') && (hisopts[TELOPT_BINARY] == OPT_NO)) {
68532097Sminshall 				state = TS_CR;
68626499Sminshall 			}
68726499Sminshall 			*pfrontp++ = c;
6886002Sroot 			break;
6896002Sroot 
6906002Sroot 		case TS_IAC:
6916002Sroot 			switch (c) {
6926002Sroot 
6936002Sroot 			/*
6946002Sroot 			 * Send the process on the pty side an
6956002Sroot 			 * interrupt.  Do this with a NULL or
6966002Sroot 			 * interrupt char; depending on the tty mode.
6976002Sroot 			 */
6986002Sroot 			case IP:
6996002Sroot 				interrupt();
7006002Sroot 				break;
7016002Sroot 
70227229Sminshall 			case BREAK:
70327229Sminshall 				sendbrk();
70427229Sminshall 				break;
70527229Sminshall 
7066002Sroot 			/*
7076002Sroot 			 * Are You There?
7086002Sroot 			 */
7096002Sroot 			case AYT:
71017583Ssam 				strcpy(nfrontp, "\r\n[Yes]\r\n");
71117583Ssam 				nfrontp += 9;
7126002Sroot 				break;
7136002Sroot 
7146002Sroot 			/*
71527185Sminshall 			 * Abort Output
71627185Sminshall 			 */
71727185Sminshall 			case AO: {
71827185Sminshall 					struct ltchars tmpltc;
71927185Sminshall 
72027185Sminshall 					ptyflush();	/* half-hearted */
72127185Sminshall 					ioctl(pty, TIOCGLTC, &tmpltc);
72227185Sminshall 					if (tmpltc.t_flushc != '\377') {
72327185Sminshall 						*pfrontp++ = tmpltc.t_flushc;
72427185Sminshall 					}
72527260Sminshall 					netclear();	/* clear buffer back */
72627185Sminshall 					*nfrontp++ = IAC;
72727185Sminshall 					*nfrontp++ = DM;
72827187Sminshall 					neturg = nfrontp-1; /* off by one XXX */
72927185Sminshall 					break;
73027185Sminshall 				}
73127185Sminshall 
73227185Sminshall 			/*
7336002Sroot 			 * Erase Character and
7346002Sroot 			 * Erase Line
7356002Sroot 			 */
7366002Sroot 			case EC:
73727185Sminshall 			case EL: {
73827185Sminshall 					struct sgttyb b;
73927185Sminshall 					char ch;
7406002Sroot 
74127185Sminshall 					ptyflush();	/* half-hearted */
74227185Sminshall 					ioctl(pty, TIOCGETP, &b);
74327185Sminshall 					ch = (c == EC) ?
74427185Sminshall 						b.sg_erase : b.sg_kill;
74527185Sminshall 					if (ch != '\377') {
74627185Sminshall 						*pfrontp++ = ch;
74727185Sminshall 					}
74827185Sminshall 					break;
74927185Sminshall 				}
75027185Sminshall 
7516002Sroot 			/*
7526002Sroot 			 * Check for urgent data...
7536002Sroot 			 */
7546002Sroot 			case DM:
75527185Sminshall 				SYNCHing = stilloob(net);
75627185Sminshall 				settimer(gotDM);
7576002Sroot 				break;
7586002Sroot 
75927185Sminshall 
7606002Sroot 			/*
7616002Sroot 			 * Begin option subnegotiation...
7626002Sroot 			 */
7636002Sroot 			case SB:
76427649Sminshall 				state = TS_SB;
7656002Sroot 				continue;
7666002Sroot 
7676002Sroot 			case WILL:
76827188Sminshall 				state = TS_WILL;
76927188Sminshall 				continue;
77027188Sminshall 
7716002Sroot 			case WONT:
77227188Sminshall 				state = TS_WONT;
77327188Sminshall 				continue;
77427188Sminshall 
7756002Sroot 			case DO:
77627188Sminshall 				state = TS_DO;
77727188Sminshall 				continue;
77827188Sminshall 
7796002Sroot 			case DONT:
78027188Sminshall 				state = TS_DONT;
7816002Sroot 				continue;
7826002Sroot 
7836002Sroot 			case IAC:
7846002Sroot 				*pfrontp++ = c;
7856002Sroot 				break;
7866002Sroot 			}
7876002Sroot 			state = TS_DATA;
7886002Sroot 			break;
7896002Sroot 
79027649Sminshall 		case TS_SB:
79127649Sminshall 			if (c == IAC) {
79227649Sminshall 				state = TS_SE;
79327649Sminshall 			} else {
79427649Sminshall 				SB_ACCUM(c);
79527649Sminshall 			}
7966002Sroot 			break;
7976002Sroot 
79827649Sminshall 		case TS_SE:
79927649Sminshall 			if (c != SE) {
80027649Sminshall 				if (c != IAC) {
80127649Sminshall 					SB_ACCUM(IAC);
80227649Sminshall 				}
80327649Sminshall 				SB_ACCUM(c);
80427649Sminshall 				state = TS_SB;
80527649Sminshall 			} else {
80627649Sminshall 				SB_TERM();
80727649Sminshall 				suboption();	/* handle sub-option */
80827649Sminshall 				state = TS_DATA;
80927649Sminshall 			}
8106002Sroot 			break;
8116002Sroot 
8126002Sroot 		case TS_WILL:
81327983Sminshall 			if (hisopts[c] != OPT_YES)
8146002Sroot 				willoption(c);
8156002Sroot 			state = TS_DATA;
8166002Sroot 			continue;
8176002Sroot 
8186002Sroot 		case TS_WONT:
81927983Sminshall 			if (hisopts[c] != OPT_NO)
8206002Sroot 				wontoption(c);
8216002Sroot 			state = TS_DATA;
8226002Sroot 			continue;
8236002Sroot 
8246002Sroot 		case TS_DO:
82527983Sminshall 			if (myopts[c] != OPT_YES)
8266002Sroot 				dooption(c);
8276002Sroot 			state = TS_DATA;
8286002Sroot 			continue;
8296002Sroot 
8306002Sroot 		case TS_DONT:
83127983Sminshall 			if (myopts[c] != OPT_NO) {
83227649Sminshall 				dontoption(c);
8336002Sroot 			}
8346002Sroot 			state = TS_DATA;
8356002Sroot 			continue;
8366002Sroot 
8376002Sroot 		default:
83827898Skarels 			syslog(LOG_ERR, "telnetd: panic state=%d\n", state);
8399218Ssam 			printf("telnetd: panic state=%d\n", state);
8406002Sroot 			exit(1);
8416002Sroot 		}
8426002Sroot 	}
8436002Sroot }
8446002Sroot 
8456002Sroot willoption(option)
8466002Sroot 	int option;
8476002Sroot {
8486002Sroot 	char *fmt;
8496002Sroot 
8506002Sroot 	switch (option) {
8516002Sroot 
8526002Sroot 	case TELOPT_BINARY:
8536002Sroot 		mode(RAW, 0);
85427188Sminshall 		fmt = doopt;
85527188Sminshall 		break;
8566002Sroot 
8576002Sroot 	case TELOPT_ECHO:
85827649Sminshall 		not42 = 0;		/* looks like a 4.2 system */
85927649Sminshall 		/*
86027649Sminshall 		 * Now, in a 4.2 system, to break them out of ECHOing
86127649Sminshall 		 * (to the terminal) mode, we need to send a "WILL ECHO".
86227649Sminshall 		 * Kludge upon kludge!
86327649Sminshall 		 */
86427983Sminshall 		if (myopts[TELOPT_ECHO] == OPT_YES) {
86527649Sminshall 		    dooption(TELOPT_ECHO);
86627649Sminshall 		}
86727649Sminshall 		fmt = dont;
86827188Sminshall 		break;
8696002Sroot 
87027649Sminshall 	case TELOPT_TTYPE:
87127983Sminshall 		settimer(ttypeopt);
87227983Sminshall 		if (hisopts[TELOPT_TTYPE] == OPT_YES_BUT_ALWAYS_LOOK) {
87327983Sminshall 		    hisopts[TELOPT_TTYPE] = OPT_YES;
87427983Sminshall 		    return;
87527983Sminshall 		}
87627983Sminshall 		fmt = doopt;
87727983Sminshall 		break;
87827983Sminshall 
8796002Sroot 	case TELOPT_SGA:
8806002Sroot 		fmt = doopt;
8816002Sroot 		break;
8826002Sroot 
8836002Sroot 	case TELOPT_TM:
8846002Sroot 		fmt = dont;
8856002Sroot 		break;
8866002Sroot 
8876002Sroot 	default:
8886002Sroot 		fmt = dont;
8896002Sroot 		break;
8906002Sroot 	}
89127188Sminshall 	if (fmt == doopt) {
89227983Sminshall 		hisopts[option] = OPT_YES;
89327188Sminshall 	} else {
89427983Sminshall 		hisopts[option] = OPT_NO;
89527188Sminshall 	}
89632452Sbostic 	(void) sprintf(nfrontp, fmt, option);
8978379Ssam 	nfrontp += sizeof (dont) - 2;
8986002Sroot }
8996002Sroot 
9006002Sroot wontoption(option)
9016002Sroot 	int option;
9026002Sroot {
9036002Sroot 	char *fmt;
9046002Sroot 
9056002Sroot 	switch (option) {
9066002Sroot 	case TELOPT_ECHO:
90727649Sminshall 		not42 = 1;		/* doesn't seem to be a 4.2 system */
90827188Sminshall 		break;
9096002Sroot 
9106002Sroot 	case TELOPT_BINARY:
9116002Sroot 		mode(0, RAW);
9126002Sroot 		break;
91328044Sminshall 
91428044Sminshall 	case TELOPT_TTYPE:
91528044Sminshall 	    settimer(ttypeopt);
91628044Sminshall 	    break;
9176002Sroot 	}
91828044Sminshall 
91927188Sminshall 	fmt = dont;
92027983Sminshall 	hisopts[option] = OPT_NO;
92132452Sbostic 	(void) sprintf(nfrontp, fmt, option);
9228379Ssam 	nfrontp += sizeof (doopt) - 2;
9236002Sroot }
9246002Sroot 
9256002Sroot dooption(option)
9266002Sroot 	int option;
9276002Sroot {
9286002Sroot 	char *fmt;
9296002Sroot 
9306002Sroot 	switch (option) {
9316002Sroot 
9326002Sroot 	case TELOPT_TM:
9336002Sroot 		fmt = wont;
9346002Sroot 		break;
9356002Sroot 
9366002Sroot 	case TELOPT_ECHO:
9376002Sroot 		mode(ECHO|CRMOD, 0);
93827188Sminshall 		fmt = will;
93927188Sminshall 		break;
9406002Sroot 
9416002Sroot 	case TELOPT_BINARY:
9426002Sroot 		mode(RAW, 0);
94327188Sminshall 		fmt = will;
94427188Sminshall 		break;
9456002Sroot 
9466002Sroot 	case TELOPT_SGA:
9476002Sroot 		fmt = will;
9486002Sroot 		break;
9496002Sroot 
9506002Sroot 	default:
9516002Sroot 		fmt = wont;
9526002Sroot 		break;
9536002Sroot 	}
95427188Sminshall 	if (fmt == will) {
95527983Sminshall 	    myopts[option] = OPT_YES;
95627188Sminshall 	} else {
95727983Sminshall 	    myopts[option] = OPT_NO;
95827188Sminshall 	}
95932452Sbostic 	(void) sprintf(nfrontp, fmt, option);
9608379Ssam 	nfrontp += sizeof (doopt) - 2;
9616002Sroot }
9626002Sroot 
96327649Sminshall 
96427649Sminshall dontoption(option)
96527649Sminshall int option;
96627649Sminshall {
96727649Sminshall     char *fmt;
96827649Sminshall 
96927649Sminshall     switch (option) {
97027649Sminshall     case TELOPT_ECHO:		/* we should stop echoing */
97132401Sminshall 	mode(0, ECHO);
97227649Sminshall 	fmt = wont;
97327649Sminshall 	break;
97427983Sminshall 
97527649Sminshall     default:
97627649Sminshall 	fmt = wont;
97727649Sminshall 	break;
97827649Sminshall     }
97927983Sminshall 
98027649Sminshall     if (fmt = wont) {
98127983Sminshall 	myopts[option] = OPT_NO;
98227649Sminshall     } else {
98327983Sminshall 	myopts[option] = OPT_YES;
98427649Sminshall     }
98532452Sbostic     (void) sprintf(nfrontp, fmt, option);
98627649Sminshall     nfrontp += sizeof (wont) - 2;
98727649Sminshall }
98827649Sminshall 
98927649Sminshall /*
99027649Sminshall  * suboption()
99127649Sminshall  *
99227649Sminshall  *	Look at the sub-option buffer, and try to be helpful to the other
99327649Sminshall  * side.
99427649Sminshall  *
99527649Sminshall  *	Currently we recognize:
99627649Sminshall  *
99727983Sminshall  *	Terminal type is
99827649Sminshall  */
99927649Sminshall 
100027649Sminshall suboption()
100127649Sminshall {
100227983Sminshall     switch (SB_GET()) {
100327983Sminshall     case TELOPT_TTYPE: {		/* Yaaaay! */
100427983Sminshall 	static char terminalname[5+41] = "TERM=";
100527983Sminshall 
100627983Sminshall 	settimer(ttypesubopt);
100727983Sminshall 
100827983Sminshall 	if (SB_GET() != TELQUAL_IS) {
100927983Sminshall 	    return;		/* ??? XXX but, this is the most robust */
101027983Sminshall 	}
101127983Sminshall 
101227983Sminshall 	terminaltype = terminalname+strlen(terminalname);
101327983Sminshall 
101427983Sminshall 	while ((terminaltype < (terminalname + sizeof terminalname-1)) &&
101527983Sminshall 								    !SB_EOF()) {
101627983Sminshall 	    register int c;
101727983Sminshall 
101827983Sminshall 	    c = SB_GET();
101927983Sminshall 	    if (isupper(c)) {
102027983Sminshall 		c = tolower(c);
102127983Sminshall 	    }
102227983Sminshall 	    *terminaltype++ = c;    /* accumulate name */
102327983Sminshall 	}
102427983Sminshall 	*terminaltype = 0;
102527983Sminshall 	terminaltype = terminalname;
102627983Sminshall 	break;
102727983Sminshall     }
102827983Sminshall 
102927649Sminshall     default:
103027649Sminshall 	;
103127649Sminshall     }
103227649Sminshall }
103327649Sminshall 
10346002Sroot mode(on, off)
10356002Sroot 	int on, off;
10366002Sroot {
10376002Sroot 	struct sgttyb b;
10386002Sroot 
10396002Sroot 	ptyflush();
10406002Sroot 	ioctl(pty, TIOCGETP, &b);
10416002Sroot 	b.sg_flags |= on;
10426002Sroot 	b.sg_flags &= ~off;
10436002Sroot 	ioctl(pty, TIOCSETP, &b);
10446002Sroot }
10456002Sroot 
10466002Sroot /*
10476002Sroot  * Send interrupt to process on other side of pty.
10486002Sroot  * If it is in raw mode, just write NULL;
10496002Sroot  * otherwise, write intr char.
10506002Sroot  */
10516002Sroot interrupt()
10526002Sroot {
10536002Sroot 	struct sgttyb b;
10546002Sroot 	struct tchars tchars;
10556002Sroot 
10566002Sroot 	ptyflush();	/* half-hearted */
10576002Sroot 	ioctl(pty, TIOCGETP, &b);
10586002Sroot 	if (b.sg_flags & RAW) {
10596002Sroot 		*pfrontp++ = '\0';
10606002Sroot 		return;
10616002Sroot 	}
10626002Sroot 	*pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ?
10636002Sroot 		'\177' : tchars.t_intrc;
10646002Sroot }
10656002Sroot 
106627229Sminshall /*
106727229Sminshall  * Send quit to process on other side of pty.
106827229Sminshall  * If it is in raw mode, just write NULL;
106927229Sminshall  * otherwise, write quit char.
107027229Sminshall  */
107127229Sminshall sendbrk()
107227229Sminshall {
107327229Sminshall 	struct sgttyb b;
107427229Sminshall 	struct tchars tchars;
107527229Sminshall 
107627229Sminshall 	ptyflush();	/* half-hearted */
107727229Sminshall 	ioctl(pty, TIOCGETP, &b);
107827229Sminshall 	if (b.sg_flags & RAW) {
107927229Sminshall 		*pfrontp++ = '\0';
108027229Sminshall 		return;
108127229Sminshall 	}
108227229Sminshall 	*pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ?
108327229Sminshall 		'\034' : tchars.t_quitc;
108427229Sminshall }
108527229Sminshall 
10866002Sroot ptyflush()
10876002Sroot {
10886002Sroot 	int n;
10896002Sroot 
10906002Sroot 	if ((n = pfrontp - pbackp) > 0)
10916002Sroot 		n = write(pty, pbackp, n);
10928346Ssam 	if (n < 0)
10938346Ssam 		return;
10946002Sroot 	pbackp += n;
10956002Sroot 	if (pbackp == pfrontp)
10966002Sroot 		pbackp = pfrontp = ptyobuf;
10976002Sroot }
109827260Sminshall 
109927260Sminshall /*
110027260Sminshall  * nextitem()
110127260Sminshall  *
110227260Sminshall  *	Return the address of the next "item" in the TELNET data
110327260Sminshall  * stream.  This will be the address of the next character if
110427260Sminshall  * the current address is a user data character, or it will
110527260Sminshall  * be the address of the character following the TELNET command
110627260Sminshall  * if the current address is a TELNET IAC ("I Am a Command")
110727260Sminshall  * character.
110827260Sminshall  */
11096002Sroot 
111027260Sminshall char *
111127260Sminshall nextitem(current)
111227260Sminshall char	*current;
11136002Sroot {
111427260Sminshall     if ((*current&0xff) != IAC) {
111527260Sminshall 	return current+1;
111627260Sminshall     }
111727260Sminshall     switch (*(current+1)&0xff) {
111827260Sminshall     case DO:
111927260Sminshall     case DONT:
112027260Sminshall     case WILL:
112127260Sminshall     case WONT:
112227260Sminshall 	return current+3;
112327260Sminshall     case SB:		/* loop forever looking for the SE */
112427260Sminshall 	{
112527260Sminshall 	    register char *look = current+2;
11266002Sroot 
112727260Sminshall 	    for (;;) {
112827260Sminshall 		if ((*look++&0xff) == IAC) {
112927260Sminshall 		    if ((*look++&0xff) == SE) {
113027260Sminshall 			return look;
113127260Sminshall 		    }
113227260Sminshall 		}
113327260Sminshall 	    }
11348346Ssam 	}
113527260Sminshall     default:
113627260Sminshall 	return current+2;
113727260Sminshall     }
11386002Sroot }
11396002Sroot 
114027185Sminshall 
114127185Sminshall /*
114227260Sminshall  * netclear()
114327260Sminshall  *
114427260Sminshall  *	We are about to do a TELNET SYNCH operation.  Clear
114527260Sminshall  * the path to the network.
114627260Sminshall  *
114727260Sminshall  *	Things are a bit tricky since we may have sent the first
114827260Sminshall  * byte or so of a previous TELNET command into the network.
114927260Sminshall  * So, we have to scan the network buffer from the beginning
115027260Sminshall  * until we are up to where we want to be.
115127260Sminshall  *
115227260Sminshall  *	A side effect of what we do, just to keep things
115327260Sminshall  * simple, is to clear the urgent data pointer.  The principal
115427260Sminshall  * caller should be setting the urgent data pointer AFTER calling
115527260Sminshall  * us in any case.
115627260Sminshall  */
115727260Sminshall 
115827260Sminshall netclear()
115927260Sminshall {
116027260Sminshall     register char *thisitem, *next;
116127260Sminshall     char *good;
116227260Sminshall #define	wewant(p)	((nfrontp > p) && ((*p&0xff) == IAC) && \
116327260Sminshall 				((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
116427260Sminshall 
116527260Sminshall     thisitem = netobuf;
116627260Sminshall 
116727260Sminshall     while ((next = nextitem(thisitem)) <= nbackp) {
116827260Sminshall 	thisitem = next;
116927260Sminshall     }
117027260Sminshall 
117127260Sminshall     /* Now, thisitem is first before/at boundary. */
117227260Sminshall 
117327260Sminshall     good = netobuf;	/* where the good bytes go */
117427260Sminshall 
117527260Sminshall     while (nfrontp > thisitem) {
117627260Sminshall 	if (wewant(thisitem)) {
117727260Sminshall 	    int length;
117827260Sminshall 
117927260Sminshall 	    next = thisitem;
118027260Sminshall 	    do {
118127260Sminshall 		next = nextitem(next);
118227260Sminshall 	    } while (wewant(next) && (nfrontp > next));
118327260Sminshall 	    length = next-thisitem;
118427260Sminshall 	    bcopy(thisitem, good, length);
118527260Sminshall 	    good += length;
118627260Sminshall 	    thisitem = next;
118727260Sminshall 	} else {
118827260Sminshall 	    thisitem = nextitem(thisitem);
118927260Sminshall 	}
119027260Sminshall     }
119127260Sminshall 
119227260Sminshall     nbackp = netobuf;
119327260Sminshall     nfrontp = good;		/* next byte to be sent */
119427260Sminshall     neturg = 0;
119527260Sminshall }
119627260Sminshall 
119727260Sminshall /*
119827185Sminshall  *  netflush
119927185Sminshall  *		Send as much data as possible to the network,
120027185Sminshall  *	handling requests for urgent data.
120127185Sminshall  */
120227185Sminshall 
120327185Sminshall 
120427185Sminshall netflush()
120527185Sminshall {
120627185Sminshall     int n;
120727185Sminshall 
120827185Sminshall     if ((n = nfrontp - nbackp) > 0) {
120927649Sminshall 	/*
121027649Sminshall 	 * if no urgent data, or if the other side appears to be an
121127649Sminshall 	 * old 4.2 client (and thus unable to survive TCP urgent data),
121227649Sminshall 	 * write the entire buffer in non-OOB mode.
121327649Sminshall 	 */
121427649Sminshall 	if ((neturg == 0) || (not42 == 0)) {
121527185Sminshall 	    n = write(net, nbackp, n);	/* normal write */
121627185Sminshall 	} else {
121727185Sminshall 	    n = neturg - nbackp;
121827185Sminshall 	    /*
121927185Sminshall 	     * In 4.2 (and 4.3) systems, there is some question about
122027185Sminshall 	     * what byte in a sendOOB operation is the "OOB" data.
122127185Sminshall 	     * To make ourselves compatible, we only send ONE byte
122227185Sminshall 	     * out of band, the one WE THINK should be OOB (though
122327185Sminshall 	     * we really have more the TCP philosophy of urgent data
122427185Sminshall 	     * rather than the Unix philosophy of OOB data).
122527185Sminshall 	     */
122627185Sminshall 	    if (n > 1) {
122727185Sminshall 		n = send(net, nbackp, n-1, 0);	/* send URGENT all by itself */
122827185Sminshall 	    } else {
122927185Sminshall 		n = send(net, nbackp, n, MSG_OOB);	/* URGENT data */
123027185Sminshall 	    }
123127185Sminshall 	}
123227185Sminshall     }
123327185Sminshall     if (n < 0) {
123427185Sminshall 	if (errno == EWOULDBLOCK)
123527185Sminshall 	    return;
123627185Sminshall 	/* should blow this guy away... */
123727185Sminshall 	return;
123827185Sminshall     }
123927185Sminshall     nbackp += n;
124027185Sminshall     if (nbackp >= neturg) {
124127185Sminshall 	neturg = 0;
124227185Sminshall     }
124327185Sminshall     if (nbackp == nfrontp) {
124427185Sminshall 	nbackp = nfrontp = netobuf;
124527185Sminshall     }
124627185Sminshall }
124727185Sminshall 
12486002Sroot cleanup()
12496002Sroot {
12506002Sroot 
12516002Sroot 	rmut();
125210008Ssam 	vhangup();	/* XXX */
125310191Ssam 	shutdown(net, 2);
12546002Sroot 	exit(1);
12556002Sroot }
12566002Sroot 
12576002Sroot #include <utmp.h>
12586002Sroot 
12596002Sroot struct	utmp wtmp;
12606002Sroot char	wtmpf[]	= "/usr/adm/wtmp";
126123567Sbloom char	utmpf[] = "/etc/utmp";
126223567Sbloom #define SCPYN(a, b)	strncpy(a, b, sizeof(a))
126323567Sbloom #define SCMPN(a, b)	strncmp(a, b, sizeof(a))
12646002Sroot 
12656002Sroot rmut()
12666002Sroot {
12676002Sroot 	register f;
12686002Sroot 	int found = 0;
126923567Sbloom 	struct utmp *u, *utmp;
127023567Sbloom 	int nutmp;
127123567Sbloom 	struct stat statbf;
12726002Sroot 
127323567Sbloom 	f = open(utmpf, O_RDWR);
12746002Sroot 	if (f >= 0) {
127523567Sbloom 		fstat(f, &statbf);
127623567Sbloom 		utmp = (struct utmp *)malloc(statbf.st_size);
127723567Sbloom 		if (!utmp)
127823567Sbloom 			syslog(LOG_ERR, "utmp malloc failed");
127923567Sbloom 		if (statbf.st_size && utmp) {
128023567Sbloom 			nutmp = read(f, utmp, statbf.st_size);
128123567Sbloom 			nutmp /= sizeof(struct utmp);
128223567Sbloom 
128323567Sbloom 			for (u = utmp ; u < &utmp[nutmp] ; u++) {
128423567Sbloom 				if (SCMPN(u->ut_line, line+5) ||
128523567Sbloom 				    u->ut_name[0]==0)
128623567Sbloom 					continue;
128723567Sbloom 				lseek(f, ((long)u)-((long)utmp), L_SET);
128823567Sbloom 				SCPYN(u->ut_name, "");
128923567Sbloom 				SCPYN(u->ut_host, "");
129023567Sbloom 				time(&u->ut_time);
129123567Sbloom 				write(f, (char *)u, sizeof(wtmp));
129223567Sbloom 				found++;
129323567Sbloom 			}
12946002Sroot 		}
12956002Sroot 		close(f);
12966002Sroot 	}
12976002Sroot 	if (found) {
129817583Ssam 		f = open(wtmpf, O_WRONLY|O_APPEND);
12996002Sroot 		if (f >= 0) {
13006002Sroot 			SCPYN(wtmp.ut_line, line+5);
13016002Sroot 			SCPYN(wtmp.ut_name, "");
130212683Ssam 			SCPYN(wtmp.ut_host, "");
13036002Sroot 			time(&wtmp.ut_time);
130423567Sbloom 			write(f, (char *)&wtmp, sizeof(wtmp));
13056002Sroot 			close(f);
13066002Sroot 		}
13076002Sroot 	}
13086002Sroot 	chmod(line, 0666);
13096002Sroot 	chown(line, 0, 0);
13106002Sroot 	line[strlen("/dev/")] = 'p';
13116002Sroot 	chmod(line, 0666);
13126002Sroot 	chown(line, 0, 0);
13136002Sroot }
1314*33271Sminshall 
1315*33271Sminshall char	editedhost[32];
1316*33271Sminshall 
1317*33271Sminshall edithost(pat, host)
1318*33271Sminshall 	register char *pat;
1319*33271Sminshall 	register char *host;
1320*33271Sminshall {
1321*33271Sminshall 	register char *res = editedhost;
1322*33271Sminshall 
1323*33271Sminshall 	if (!pat)
1324*33271Sminshall 		pat = "";
1325*33271Sminshall 	while (*pat) {
1326*33271Sminshall 		switch (*pat) {
1327*33271Sminshall 
1328*33271Sminshall 		case '#':
1329*33271Sminshall 			if (*host)
1330*33271Sminshall 				host++;
1331*33271Sminshall 			break;
1332*33271Sminshall 
1333*33271Sminshall 		case '@':
1334*33271Sminshall 			if (*host)
1335*33271Sminshall 				*res++ = *host++;
1336*33271Sminshall 			break;
1337*33271Sminshall 
1338*33271Sminshall 		default:
1339*33271Sminshall 			*res++ = *pat;
1340*33271Sminshall 			break;
1341*33271Sminshall 
1342*33271Sminshall 		}
1343*33271Sminshall 		if (res == &editedhost[sizeof editedhost - 1]) {
1344*33271Sminshall 			*res = '\0';
1345*33271Sminshall 			return;
1346*33271Sminshall 		}
1347*33271Sminshall 		pat++;
1348*33271Sminshall 	}
1349*33271Sminshall 	if (*host)
1350*33271Sminshall 		strncpy(res, host, sizeof editedhost - (res - editedhost) - 1);
1351*33271Sminshall 	else
1352*33271Sminshall 		*res = '\0';
1353*33271Sminshall 	editedhost[sizeof editedhost - 1] = '\0';
1354*33271Sminshall }
1355*33271Sminshall 
1356*33271Sminshall static char *putlocation;
1357*33271Sminshall 
1358*33271Sminshall puts(s)
1359*33271Sminshall register char *s;
1360*33271Sminshall {
1361*33271Sminshall 
1362*33271Sminshall 	while (*s)
1363*33271Sminshall 		putchr(*s++);
1364*33271Sminshall }
1365*33271Sminshall 
1366*33271Sminshall putchr(cc)
1367*33271Sminshall {
1368*33271Sminshall 	*putlocation++ = cc;
1369*33271Sminshall }
1370*33271Sminshall 
1371*33271Sminshall putf(cp, where, tty)
1372*33271Sminshall register char *cp;
1373*33271Sminshall char *where;
1374*33271Sminshall int tty;
1375*33271Sminshall {
1376*33271Sminshall 	char *slash;
1377*33271Sminshall 	char datebuffer[60];
1378*33271Sminshall 	extern char *rindex();
1379*33271Sminshall 
1380*33271Sminshall 	putlocation = where;
1381*33271Sminshall 
1382*33271Sminshall 	while (*cp) {
1383*33271Sminshall 		if (*cp != '%') {
1384*33271Sminshall 			putchr(*cp++);
1385*33271Sminshall 			continue;
1386*33271Sminshall 		}
1387*33271Sminshall 		switch (*++cp) {
1388*33271Sminshall 
1389*33271Sminshall 		case 't':
1390*33271Sminshall 			slash = rindex(line, '/');
1391*33271Sminshall 			if (slash == (char *) 0)
1392*33271Sminshall 				puts(line);
1393*33271Sminshall 			else
1394*33271Sminshall 				puts(&slash[1]);
1395*33271Sminshall 			break;
1396*33271Sminshall 
1397*33271Sminshall 		case 'h':
1398*33271Sminshall 			puts(editedhost);
1399*33271Sminshall 			break;
1400*33271Sminshall 
1401*33271Sminshall 		case 'd':
1402*33271Sminshall 			get_date(datebuffer);
1403*33271Sminshall 			puts(datebuffer);
1404*33271Sminshall 			break;
1405*33271Sminshall 
1406*33271Sminshall 		case '%':
1407*33271Sminshall 			putchr('%');
1408*33271Sminshall 			break;
1409*33271Sminshall 		}
1410*33271Sminshall 		cp++;
1411*33271Sminshall 	}
1412*33271Sminshall }
1413