xref: /csrg-svn/libexec/telnetd/telnetd.c (revision 27260)
121182Sdist /*
221182Sdist  * Copyright (c) 1983 Regents of the University of California.
321182Sdist  * All rights reserved.  The Berkeley software License Agreement
421182Sdist  * specifies the terms and conditions for redistribution.
521182Sdist  */
621182Sdist 
76295Sroot #ifndef lint
821182Sdist char copyright[] =
921182Sdist "@(#) Copyright (c) 1983 Regents of the University of California.\n\
1021182Sdist  All rights reserved.\n";
1121182Sdist #endif not lint
126295Sroot 
1321182Sdist #ifndef lint
14*27260Sminshall static char sccsid[] = "@(#)telnetd.c	5.13 (Berkeley) 04/22/86";
1521182Sdist #endif not lint
1621182Sdist 
176002Sroot /*
186002Sroot  * Stripped-down telnet server.
196002Sroot  */
209218Ssam #include <sys/types.h>
219218Ssam #include <sys/socket.h>
2213608Ssam #include <sys/wait.h>
2317583Ssam #include <sys/file.h>
2420188Skarels #include <sys/stat.h>
2527185Sminshall #include <sys/time.h>
269218Ssam 
279218Ssam #include <netinet/in.h>
289218Ssam 
2912216Ssam #include <arpa/telnet.h>
3012216Ssam 
316002Sroot #include <stdio.h>
326002Sroot #include <signal.h>
336002Sroot #include <errno.h>
346002Sroot #include <sgtty.h>
358346Ssam #include <netdb.h>
3617187Sralph #include <syslog.h>
379218Ssam 
3813798Ssam #define	BELL	'\07'
3923567Sbloom #define BANNER	"\r\n\r\n4.3 BSD UNIX (%s)\r\n\r\r\n\r%s"
406002Sroot 
416002Sroot char	hisopts[256];
426002Sroot char	myopts[256];
436002Sroot 
446002Sroot char	doopt[] = { IAC, DO, '%', 'c', 0 };
456002Sroot char	dont[] = { IAC, DONT, '%', 'c', 0 };
466002Sroot char	will[] = { IAC, WILL, '%', 'c', 0 };
476002Sroot char	wont[] = { IAC, WONT, '%', 'c', 0 };
486002Sroot 
496002Sroot /*
506002Sroot  * I/O data buffers, pointers, and counters.
516002Sroot  */
526002Sroot char	ptyibuf[BUFSIZ], *ptyip = ptyibuf;
536002Sroot char	ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf;
546002Sroot char	netibuf[BUFSIZ], *netip = netibuf;
556388Ssam char	netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf;
5627185Sminshall char	*neturg = 0;		/* one past last bye of urgent data */
576002Sroot int	pcc, ncc;
586002Sroot 
596002Sroot int	pty, net;
606002Sroot int	inter;
6113799Ssam extern	char **environ;
626002Sroot extern	int errno;
6320188Skarels char	*line;
6427185Sminshall int	SYNCHing = 0;		/* we are in TELNET SYNCH mode */
6527185Sminshall /*
6627185Sminshall  * The following are some clocks used to decide how to interpret
6727185Sminshall  * the relationship between various variables.
6827185Sminshall  */
696002Sroot 
7027185Sminshall struct {
7127185Sminshall     int
7227185Sminshall 	system,			/* what the current time is */
7327185Sminshall 	echotoggle,		/* last time user entered echo character */
7427185Sminshall 	modenegotiated,		/* last time operating mode negotiated */
7527185Sminshall 	didnetreceive,		/* last time we read data from network */
7627185Sminshall 	gotDM;			/* when did we last see a data mark */
7727185Sminshall } clocks;
7827185Sminshall 
7927185Sminshall #define	settimer(x)	clocks.x = clocks.system++
8027185Sminshall 
816002Sroot main(argc, argv)
826002Sroot 	char *argv[];
836002Sroot {
8416371Skarels 	struct sockaddr_in from;
8517156Ssam 	int on = 1, fromlen;
866002Sroot 
8727185Sminshall #if	defined(DEBUG)
8827185Sminshall 	{
8927185Sminshall 	    int s, ns, foo;
9027185Sminshall 	    struct servent *sp;
9127185Sminshall 	    static struct sockaddr_in sin = { AF_INET };
9227185Sminshall 
9327185Sminshall 	    sp = getservbyname("telnet", "tcp");
9427185Sminshall 	    if (sp == 0) {
9527185Sminshall 		    fprintf(stderr, "telnetd: tcp/telnet: unknown service\n");
9627185Sminshall 		    exit(1);
9727185Sminshall 	    }
9827185Sminshall 	    sin.sin_port = sp->s_port;
9927185Sminshall 	    argc--, argv++;
10027185Sminshall 	    if (argc > 0) {
10127185Sminshall 		    sin.sin_port = atoi(*argv);
10227185Sminshall 		    sin.sin_port = htons((u_short)sin.sin_port);
10327185Sminshall 	    }
10427185Sminshall 
10527185Sminshall 	    s = socket(AF_INET, SOCK_STREAM, 0);
10627185Sminshall 	    if (s < 0) {
10727185Sminshall 		    perror("telnetd: socket");;
10827185Sminshall 		    exit(1);
10927185Sminshall 	    }
11027185Sminshall 	    if (bind(s, &sin, sizeof sin) < 0) {
11127185Sminshall 		perror("bind");
11227185Sminshall 		exit(1);
11327185Sminshall 	    }
11427185Sminshall 	    if (listen(s, 1) < 0) {
11527185Sminshall 		perror("listen");
11627185Sminshall 		exit(1);
11727185Sminshall 	    }
11827185Sminshall 	    foo = sizeof sin;
11927185Sminshall 	    ns = accept(s, &sin, &foo);
12027185Sminshall 	    if (ns < 0) {
12127185Sminshall 		perror("accept");
12227185Sminshall 		exit(1);
12327185Sminshall 	    }
12427185Sminshall 	    dup2(ns, 0);
12527185Sminshall 	    close(s);
12627185Sminshall 	}
12727185Sminshall #endif	/* defined(DEBUG) */
12824855Seric 	openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON);
12916371Skarels 	fromlen = sizeof (from);
13016371Skarels 	if (getpeername(0, &from, &fromlen) < 0) {
13116371Skarels 		fprintf(stderr, "%s: ", argv[0]);
13216371Skarels 		perror("getpeername");
13316371Skarels 		_exit(1);
1348346Ssam 	}
13517156Ssam 	if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) {
13617187Sralph 		syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
13710418Ssam 	}
13816371Skarels 	doit(0, &from);
1396002Sroot }
1406002Sroot 
14113799Ssam char	*envinit[] = { "TERM=network", 0 };
1426002Sroot int	cleanup();
1436002Sroot 
1446002Sroot /*
1456002Sroot  * Get a pty, scan input lines.
1466002Sroot  */
14712683Ssam doit(f, who)
14812683Ssam 	int f;
14912683Ssam 	struct sockaddr_in *who;
1506002Sroot {
15120188Skarels 	char *host, *inet_ntoa();
15217583Ssam 	int i, p, t;
1536002Sroot 	struct sgttyb b;
15412683Ssam 	struct hostent *hp;
15520188Skarels 	char c;
1566002Sroot 
15720188Skarels 	for (c = 'p'; c <= 's'; c++) {
15820188Skarels 		struct stat stb;
15920188Skarels 
16020188Skarels 		line = "/dev/ptyXX";
16120188Skarels 		line[strlen("/dev/pty")] = c;
16220188Skarels 		line[strlen("/dev/ptyp")] = '0';
16320188Skarels 		if (stat(line, &stb) < 0)
16420188Skarels 			break;
16517583Ssam 		for (i = 0; i < 16; i++) {
16620188Skarels 			line[strlen("/dev/ptyp")] = "0123456789abcdef"[i];
16720188Skarels 			p = open(line, 2);
16817583Ssam 			if (p > 0)
16917583Ssam 				goto gotpty;
17017583Ssam 		}
1716002Sroot 	}
1729244Ssam 	fatal(f, "All network ports in use");
1739244Ssam 	/*NOTREACHED*/
1746002Sroot gotpty:
1756002Sroot 	dup2(f, 0);
17620188Skarels 	line[strlen("/dev/")] = 't';
17717583Ssam 	t = open("/dev/tty", O_RDWR);
1786002Sroot 	if (t >= 0) {
1796002Sroot 		ioctl(t, TIOCNOTTY, 0);
1806002Sroot 		close(t);
1816002Sroot 	}
18220188Skarels 	t = open(line, O_RDWR);
1839244Ssam 	if (t < 0)
18420188Skarels 		fatalperror(f, line, errno);
1856002Sroot 	ioctl(t, TIOCGETP, &b);
1866388Ssam 	b.sg_flags = CRMOD|XTABS|ANYP;
1876002Sroot 	ioctl(t, TIOCSETP, &b);
1886388Ssam 	ioctl(p, TIOCGETP, &b);
1898379Ssam 	b.sg_flags &= ~ECHO;
1906388Ssam 	ioctl(p, TIOCSETP, &b);
19112683Ssam 	hp = gethostbyaddr(&who->sin_addr, sizeof (struct in_addr),
19212683Ssam 		who->sin_family);
19312683Ssam 	if (hp)
19412683Ssam 		host = hp->h_name;
19512683Ssam 	else
19617444Sralph 		host = inet_ntoa(who->sin_addr);
1979244Ssam 	if ((i = fork()) < 0)
1989244Ssam 		fatalperror(f, "fork", errno);
1996002Sroot 	if (i)
2006002Sroot 		telnet(f, p);
2016002Sroot 	close(f);
2026002Sroot 	close(p);
2036002Sroot 	dup2(t, 0);
2046002Sroot 	dup2(t, 1);
2056002Sroot 	dup2(t, 2);
2066002Sroot 	close(t);
20713799Ssam 	environ = envinit;
20812713Ssam 	execl("/bin/login", "login", "-h", host, 0);
2099244Ssam 	fatalperror(f, "/bin/login", errno);
2109244Ssam 	/*NOTREACHED*/
2119244Ssam }
2129244Ssam 
2139244Ssam fatal(f, msg)
2149244Ssam 	int f;
2159244Ssam 	char *msg;
2169244Ssam {
2179244Ssam 	char buf[BUFSIZ];
2189244Ssam 
21917583Ssam 	(void) sprintf(buf, "telnetd: %s.\r\n", msg);
2209244Ssam 	(void) write(f, buf, strlen(buf));
2216002Sroot 	exit(1);
2226002Sroot }
2236002Sroot 
2249244Ssam fatalperror(f, msg, errno)
2259244Ssam 	int f;
2269244Ssam 	char *msg;
2279244Ssam 	int errno;
2289244Ssam {
2299244Ssam 	char buf[BUFSIZ];
2309244Ssam 	extern char *sys_errlist[];
2319244Ssam 
23217583Ssam 	(void) sprintf(buf, "%s: %s\r\n", msg, sys_errlist[errno]);
2339244Ssam 	fatal(f, buf);
2349244Ssam }
2359244Ssam 
23627185Sminshall 
2376002Sroot /*
23827185Sminshall  * Check a descriptor to see if out of band data exists on it.
23927185Sminshall  */
24027185Sminshall 
24127185Sminshall 
24227185Sminshall stilloob(s)
24327185Sminshall int	s;		/* socket number */
24427185Sminshall {
24527185Sminshall     static struct timeval timeout = { 0 };
24627185Sminshall     fd_set	excepts;
24727185Sminshall     int value;
24827185Sminshall 
24927185Sminshall     do {
25027185Sminshall 	FD_ZERO(&excepts);
25127185Sminshall 	FD_SET(s, &excepts);
25227185Sminshall 	value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout);
25327185Sminshall     } while ((value == -1) && (errno = EINTR));
25427185Sminshall 
25527185Sminshall     if (value < 0) {
25627185Sminshall 	fatalperror(pty, "select", errno);
25727185Sminshall     }
25827185Sminshall     if (FD_ISSET(s, &excepts)) {
25927185Sminshall 	return 1;
26027185Sminshall     } else {
26127185Sminshall 	return 0;
26227185Sminshall     }
26327185Sminshall }
26427185Sminshall 
26527185Sminshall /*
2666002Sroot  * Main loop.  Select from pty and network, and
2676002Sroot  * hand data to telnet receiver finite state machine.
2686002Sroot  */
2696002Sroot telnet(f, p)
2706002Sroot {
2716002Sroot 	int on = 1;
27212713Ssam 	char hostname[32];
2736002Sroot 
2746002Sroot 	net = f, pty = p;
2756002Sroot 	ioctl(f, FIONBIO, &on);
2766002Sroot 	ioctl(p, FIONBIO, &on);
27727229Sminshall #if	defined(xxxSO_OOBINLINE)
27827229Sminshall 	setsockopt(net, SOL_SOCKET, SO_OOBINLINE, on, sizeof on);
27927229Sminshall #endif	/* defined(xxxSO_OOBINLINE) */
2806002Sroot 	signal(SIGTSTP, SIG_IGN);
28113028Ssam 	signal(SIGCHLD, cleanup);
28226083Slepreau 	setpgrp(0, 0);
2836002Sroot 
2848379Ssam 	/*
28527185Sminshall 	 * Request to do remote echo and to suppress go ahead.
2868379Ssam 	 */
2878379Ssam 	dooption(TELOPT_ECHO);
28827185Sminshall 	dooption(TELOPT_SGA);
28912713Ssam 	/*
29012713Ssam 	 * Show banner that getty never gave.
29112713Ssam 	 */
29212713Ssam 	gethostname(hostname, sizeof (hostname));
29312713Ssam 	sprintf(nfrontp, BANNER, hostname, "");
29412713Ssam 	nfrontp += strlen(nfrontp);
2956002Sroot 	for (;;) {
29627185Sminshall 		fd_set ibits, obits, xbits;
2976002Sroot 		register int c;
2986002Sroot 
29927185Sminshall 		if (ncc < 0 && pcc < 0)
30027185Sminshall 			break;
30127185Sminshall 
30227185Sminshall 		FD_ZERO(&ibits);
30327185Sminshall 		FD_ZERO(&obits);
30427185Sminshall 		FD_ZERO(&xbits);
3056002Sroot 		/*
3066002Sroot 		 * Never look for input if there's still
3076002Sroot 		 * stuff in the corresponding output buffer
3086002Sroot 		 */
30927185Sminshall 		if (nfrontp - nbackp || pcc > 0) {
31027185Sminshall 			FD_SET(f, &obits);
31127185Sminshall 		} else {
31227185Sminshall 			FD_SET(p, &ibits);
31327185Sminshall 		}
31427185Sminshall 		if (pfrontp - pbackp || ncc > 0) {
31527185Sminshall 			FD_SET(p, &obits);
31627185Sminshall 		} else {
31727185Sminshall 			FD_SET(f, &ibits);
31827185Sminshall 		}
31927185Sminshall 		if (!SYNCHing) {
32027185Sminshall 			FD_SET(f, &xbits);
32127185Sminshall 		}
32227185Sminshall 		if ((c = select(16, &ibits, &obits, &xbits,
32327185Sminshall 						(struct timeval *)0)) < 1) {
32427185Sminshall 			if (c == -1) {
32527185Sminshall 				if (errno == EINTR) {
32627185Sminshall 					continue;
32727185Sminshall 				}
32827185Sminshall 			}
3296002Sroot 			sleep(5);
3306002Sroot 			continue;
3316002Sroot 		}
3326002Sroot 
3336002Sroot 		/*
33427185Sminshall 		 * Any urgent data?
33527185Sminshall 		 */
33627185Sminshall 		if (FD_ISSET(net, &xbits)) {
33727185Sminshall 		    SYNCHing = 1;
33827185Sminshall 		}
33927185Sminshall 
34027185Sminshall 		/*
3416002Sroot 		 * Something to read from the network...
3426002Sroot 		 */
34327185Sminshall 		if (FD_ISSET(net, &ibits)) {
34427229Sminshall #if	!defined(xxxSO_OOBINLINE)
34527185Sminshall 			/*
34627185Sminshall 			 * In 4.2 (and some early 4.3) systems, the
34727185Sminshall 			 * OOB indication and data handling in the kernel
34827185Sminshall 			 * is such that if two separate TCP Urgent requests
34927185Sminshall 			 * come in, one byte of TCP data will be overlaid.
35027185Sminshall 			 * This is fatal for Telnet, but we try to live
35127185Sminshall 			 * with it.
35227185Sminshall 			 *
35327185Sminshall 			 * In addition, in 4.2 (and...), a special protocol
35427185Sminshall 			 * is needed to pick up the TCP Urgent data in
35527185Sminshall 			 * the correct sequence.
35627185Sminshall 			 *
35727185Sminshall 			 * What we do is:  if we think we are in urgent
35827185Sminshall 			 * mode, we look to see if we are "at the mark".
35927185Sminshall 			 * If we are, we do an OOB receive.  If we run
36027185Sminshall 			 * this twice, we will do the OOB receive twice,
36127185Sminshall 			 * but the second will fail, since the second
36227185Sminshall 			 * time we were "at the mark", but there wasn't
36327185Sminshall 			 * any data there (the kernel doesn't reset
36427185Sminshall 			 * "at the mark" until we do a normal read).
36527185Sminshall 			 * Once we've read the OOB data, we go ahead
36627185Sminshall 			 * and do normal reads.
36727185Sminshall 			 *
36827185Sminshall 			 * There is also another problem, which is that
36927185Sminshall 			 * since the OOB byte we read doesn't put us
37027185Sminshall 			 * out of OOB state, and since that byte is most
37127185Sminshall 			 * likely the TELNET DM (data mark), we would
37227185Sminshall 			 * stay in the TELNET SYNCH (SYNCHing) state.
37327185Sminshall 			 * So, clocks to the rescue.  If we've "just"
37427185Sminshall 			 * received a DM, then we test for the
37527185Sminshall 			 * presence of OOB data when the receive OOB
37627185Sminshall 			 * fails (and AFTER we did the normal mode read
37727185Sminshall 			 * to clear "at the mark").
37827185Sminshall 			 */
37927185Sminshall 		    if (SYNCHing) {
38027185Sminshall 			int atmark;
38127185Sminshall 
38227185Sminshall 			ioctl(net, SIOCATMARK, (char *)&atmark);
38327185Sminshall 			if (atmark) {
38427185Sminshall 			    ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB);
38527185Sminshall 			    if ((ncc == -1) && (errno == EINVAL)) {
38627185Sminshall 				ncc = read(net, netibuf, sizeof (netibuf));
38727185Sminshall 				if (clocks.didnetreceive < clocks.gotDM) {
38827185Sminshall 				    SYNCHing = stilloob(net);
38927185Sminshall 				}
39027185Sminshall 			    }
39127185Sminshall 			} else {
39227185Sminshall 			    ncc = read(net, netibuf, sizeof (netibuf));
3936002Sroot 			}
39427185Sminshall 		    } else {
39527185Sminshall 			ncc = read(net, netibuf, sizeof (netibuf));
39627185Sminshall 		    }
39727185Sminshall 		    settimer(didnetreceive);
39827229Sminshall #else	/* !defined(xxxSO_OOBINLINE)) */
39927185Sminshall 		    ncc = read(net, netibuf, sizeof (netibuf));
40027229Sminshall #endif	/* !defined(xxxSO_OOBINLINE)) */
40127185Sminshall 		    if (ncc < 0 && errno == EWOULDBLOCK)
40227185Sminshall 			ncc = 0;
40327185Sminshall 		    else {
40427185Sminshall 			if (ncc <= 0) {
40527185Sminshall 			    break;
40627185Sminshall 			}
40727185Sminshall 			netip = netibuf;
40827185Sminshall 		    }
4096002Sroot 		}
4106002Sroot 
4116002Sroot 		/*
4126002Sroot 		 * Something to read from the pty...
4136002Sroot 		 */
41427185Sminshall 		if (FD_ISSET(p, &ibits)) {
4156002Sroot 			pcc = read(p, ptyibuf, BUFSIZ);
4166002Sroot 			if (pcc < 0 && errno == EWOULDBLOCK)
4176002Sroot 				pcc = 0;
4186002Sroot 			else {
4196002Sroot 				if (pcc <= 0)
4206002Sroot 					break;
4216002Sroot 				ptyip = ptyibuf;
4226002Sroot 			}
4236002Sroot 		}
4246002Sroot 
4256002Sroot 		while (pcc > 0) {
4266002Sroot 			if ((&netobuf[BUFSIZ] - nfrontp) < 2)
4276002Sroot 				break;
4286002Sroot 			c = *ptyip++ & 0377, pcc--;
4296002Sroot 			if (c == IAC)
4306002Sroot 				*nfrontp++ = c;
4316002Sroot 			*nfrontp++ = c;
43227020Sminshall 			if (c == '\r') {
43327020Sminshall 				if (pcc > 0 && ((*ptyip & 0377) == '\n')) {
43427020Sminshall 					*nfrontp++ = *ptyip++ & 0377;
43527020Sminshall 					pcc--;
43627020Sminshall 				} else
43727020Sminshall 					*nfrontp++ = '\0';
43827020Sminshall 			}
4396002Sroot 		}
44027185Sminshall 		if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0)
4416002Sroot 			netflush();
4426002Sroot 		if (ncc > 0)
4436002Sroot 			telrcv();
44427185Sminshall 		if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0)
4456002Sroot 			ptyflush();
4466002Sroot 	}
4476002Sroot 	cleanup();
4486002Sroot }
4496002Sroot 
4506002Sroot /*
4516002Sroot  * State for recv fsm
4526002Sroot  */
4536002Sroot #define	TS_DATA		0	/* base state */
4546002Sroot #define	TS_IAC		1	/* look for double IAC's */
4556002Sroot #define	TS_CR		2	/* CR-LF ->'s CR */
4566002Sroot #define	TS_BEGINNEG	3	/* throw away begin's... */
4576002Sroot #define	TS_ENDNEG	4	/* ...end's (suboption negotiation) */
4586002Sroot #define	TS_WILL		5	/* will option negotiation */
4596002Sroot #define	TS_WONT		6	/* wont " */
4606002Sroot #define	TS_DO		7	/* do " */
4616002Sroot #define	TS_DONT		8	/* dont " */
4626002Sroot 
4636002Sroot telrcv()
4646002Sroot {
4656002Sroot 	register int c;
4666002Sroot 	static int state = TS_DATA;
4676002Sroot 
4686002Sroot 	while (ncc > 0) {
4696002Sroot 		if ((&ptyobuf[BUFSIZ] - pfrontp) < 2)
4706002Sroot 			return;
4716002Sroot 		c = *netip++ & 0377, ncc--;
4726002Sroot 		switch (state) {
4736002Sroot 
47426090Sminshall 		case TS_CR:
47526090Sminshall 			state = TS_DATA;
47626499Sminshall 			if ((c == 0) || (c == '\n')) {
47726090Sminshall 				break;
47826499Sminshall 			}
47926090Sminshall 			/* FALL THROUGH */
48026090Sminshall 
4816002Sroot 		case TS_DATA:
4826002Sroot 			if (c == IAC) {
4836002Sroot 				state = TS_IAC;
4846002Sroot 				break;
4856002Sroot 			}
4866002Sroot 			if (inter > 0)
4876002Sroot 				break;
48827020Sminshall 			/*
48927020Sminshall 			 * We map \r\n ==> \n, since \r\n says
49027020Sminshall 			 * that we want to be in column 1 of the next
49127020Sminshall 			 * printable line, and \n is the standard
49227020Sminshall 			 * unix way of saying that (\r is only good
49327020Sminshall 			 * if CRMOD is set, which it normally is).
49427020Sminshall 			 */
49526499Sminshall 			if (!myopts[TELOPT_BINARY] && c == '\r') {
49627020Sminshall 				if ((ncc > 0) && ('\n' == *netip)) {
49727020Sminshall 					netip++; ncc--;
49827020Sminshall 					c = '\n';
49927020Sminshall 				} else {
50027020Sminshall 					state = TS_CR;
50127020Sminshall 				}
50226499Sminshall 			}
50326499Sminshall 			*pfrontp++ = c;
5046002Sroot 			break;
5056002Sroot 
5066002Sroot 		case TS_IAC:
5076002Sroot 			switch (c) {
5086002Sroot 
5096002Sroot 			/*
5106002Sroot 			 * Send the process on the pty side an
5116002Sroot 			 * interrupt.  Do this with a NULL or
5126002Sroot 			 * interrupt char; depending on the tty mode.
5136002Sroot 			 */
5146002Sroot 			case IP:
5156002Sroot 				interrupt();
5166002Sroot 				break;
5176002Sroot 
51827229Sminshall 			case BREAK:
51927229Sminshall 				sendbrk();
52027229Sminshall 				break;
52127229Sminshall 
5226002Sroot 			/*
5236002Sroot 			 * Are You There?
5246002Sroot 			 */
5256002Sroot 			case AYT:
52617583Ssam 				strcpy(nfrontp, "\r\n[Yes]\r\n");
52717583Ssam 				nfrontp += 9;
5286002Sroot 				break;
5296002Sroot 
5306002Sroot 			/*
53127185Sminshall 			 * Abort Output
53227185Sminshall 			 */
53327185Sminshall 			case AO: {
53427185Sminshall 					struct ltchars tmpltc;
53527185Sminshall 
53627185Sminshall 					ptyflush();	/* half-hearted */
53727185Sminshall 					ioctl(pty, TIOCGLTC, &tmpltc);
53827185Sminshall 					if (tmpltc.t_flushc != '\377') {
53927185Sminshall 						*pfrontp++ = tmpltc.t_flushc;
54027185Sminshall 					}
541*27260Sminshall 					netclear();	/* clear buffer back */
54227185Sminshall 					*nfrontp++ = IAC;
54327185Sminshall 					*nfrontp++ = DM;
54427187Sminshall 					neturg = nfrontp-1; /* off by one XXX */
54527185Sminshall 					break;
54627185Sminshall 				}
54727185Sminshall 
54827185Sminshall 			/*
5496002Sroot 			 * Erase Character and
5506002Sroot 			 * Erase Line
5516002Sroot 			 */
5526002Sroot 			case EC:
55327185Sminshall 			case EL: {
55427185Sminshall 					struct sgttyb b;
55527185Sminshall 					char ch;
5566002Sroot 
55727185Sminshall 					ptyflush();	/* half-hearted */
55827185Sminshall 					ioctl(pty, TIOCGETP, &b);
55927185Sminshall 					ch = (c == EC) ?
56027185Sminshall 						b.sg_erase : b.sg_kill;
56127185Sminshall 					if (ch != '\377') {
56227185Sminshall 						*pfrontp++ = ch;
56327185Sminshall 					}
56427185Sminshall 					break;
56527185Sminshall 				}
56627185Sminshall 
5676002Sroot 			/*
5686002Sroot 			 * Check for urgent data...
5696002Sroot 			 */
5706002Sroot 			case DM:
57127185Sminshall 				SYNCHing = stilloob(net);
57227185Sminshall 				settimer(gotDM);
5736002Sroot 				break;
5746002Sroot 
57527185Sminshall 
5766002Sroot 			/*
5776002Sroot 			 * Begin option subnegotiation...
5786002Sroot 			 */
5796002Sroot 			case SB:
5806002Sroot 				state = TS_BEGINNEG;
5816002Sroot 				continue;
5826002Sroot 
5836002Sroot 			case WILL:
58427188Sminshall 				state = TS_WILL;
58527188Sminshall 				continue;
58627188Sminshall 
5876002Sroot 			case WONT:
58827188Sminshall 				state = TS_WONT;
58927188Sminshall 				continue;
59027188Sminshall 
5916002Sroot 			case DO:
59227188Sminshall 				state = TS_DO;
59327188Sminshall 				continue;
59427188Sminshall 
5956002Sroot 			case DONT:
59627188Sminshall 				state = TS_DONT;
5976002Sroot 				continue;
5986002Sroot 
5996002Sroot 			case IAC:
6006002Sroot 				*pfrontp++ = c;
6016002Sroot 				break;
6026002Sroot 			}
6036002Sroot 			state = TS_DATA;
6046002Sroot 			break;
6056002Sroot 
6066002Sroot 		case TS_BEGINNEG:
6076002Sroot 			if (c == IAC)
6086002Sroot 				state = TS_ENDNEG;
6096002Sroot 			break;
6106002Sroot 
6116002Sroot 		case TS_ENDNEG:
6126002Sroot 			state = c == SE ? TS_DATA : TS_BEGINNEG;
6136002Sroot 			break;
6146002Sroot 
6156002Sroot 		case TS_WILL:
6166002Sroot 			if (!hisopts[c])
6176002Sroot 				willoption(c);
6186002Sroot 			state = TS_DATA;
6196002Sroot 			continue;
6206002Sroot 
6216002Sroot 		case TS_WONT:
6226002Sroot 			if (hisopts[c])
6236002Sroot 				wontoption(c);
6246002Sroot 			state = TS_DATA;
6256002Sroot 			continue;
6266002Sroot 
6276002Sroot 		case TS_DO:
6286002Sroot 			if (!myopts[c])
6296002Sroot 				dooption(c);
6306002Sroot 			state = TS_DATA;
6316002Sroot 			continue;
6326002Sroot 
6336002Sroot 		case TS_DONT:
6346002Sroot 			if (myopts[c]) {
6356002Sroot 				myopts[c] = 0;
6366002Sroot 				sprintf(nfrontp, wont, c);
6378379Ssam 				nfrontp += sizeof (wont) - 2;
6386002Sroot 			}
6396002Sroot 			state = TS_DATA;
6406002Sroot 			continue;
6416002Sroot 
6426002Sroot 		default:
6439218Ssam 			printf("telnetd: panic state=%d\n", state);
6446002Sroot 			exit(1);
6456002Sroot 		}
6466002Sroot 	}
6476002Sroot }
6486002Sroot 
6496002Sroot willoption(option)
6506002Sroot 	int option;
6516002Sroot {
6526002Sroot 	char *fmt;
6536002Sroot 
6546002Sroot 	switch (option) {
6556002Sroot 
6566002Sroot 	case TELOPT_BINARY:
6576002Sroot 		mode(RAW, 0);
65827188Sminshall 		fmt = doopt;
65927188Sminshall 		break;
6606002Sroot 
6616002Sroot 	case TELOPT_ECHO:
6626002Sroot 		mode(0, ECHO|CRMOD);
66327188Sminshall 		fmt = doopt;
66427188Sminshall 		break;
6656002Sroot 
6666002Sroot 	case TELOPT_SGA:
6676002Sroot 		fmt = doopt;
6686002Sroot 		break;
6696002Sroot 
6706002Sroot 	case TELOPT_TM:
6716002Sroot 		fmt = dont;
6726002Sroot 		break;
6736002Sroot 
6746002Sroot 	default:
6756002Sroot 		fmt = dont;
6766002Sroot 		break;
6776002Sroot 	}
67827188Sminshall 	if (fmt == doopt) {
67927188Sminshall 		hisopts[option] = 1;
68027188Sminshall 	} else {
68127188Sminshall 		hisopts[option] = 0;
68227188Sminshall 	}
6836023Ssam 	sprintf(nfrontp, fmt, option);
6848379Ssam 	nfrontp += sizeof (dont) - 2;
6856002Sroot }
6866002Sroot 
6876002Sroot wontoption(option)
6886002Sroot 	int option;
6896002Sroot {
6906002Sroot 	char *fmt;
6916002Sroot 
6926002Sroot 	switch (option) {
6936002Sroot 	case TELOPT_ECHO:
6946002Sroot 		mode(ECHO|CRMOD, 0);
69527188Sminshall 		break;
6966002Sroot 
6976002Sroot 	case TELOPT_BINARY:
6986002Sroot 		mode(0, RAW);
6996002Sroot 		break;
7006002Sroot 	}
70127188Sminshall 	fmt = dont;
70227188Sminshall 	hisopts[option] = 0;
7036002Sroot 	sprintf(nfrontp, fmt, option);
7048379Ssam 	nfrontp += sizeof (doopt) - 2;
7056002Sroot }
7066002Sroot 
7076002Sroot dooption(option)
7086002Sroot 	int option;
7096002Sroot {
7106002Sroot 	char *fmt;
7116002Sroot 
7126002Sroot 	switch (option) {
7136002Sroot 
7146002Sroot 	case TELOPT_TM:
7156002Sroot 		fmt = wont;
7166002Sroot 		break;
7176002Sroot 
7186002Sroot 	case TELOPT_ECHO:
7196002Sroot 		mode(ECHO|CRMOD, 0);
72027188Sminshall 		fmt = will;
72127188Sminshall 		break;
7226002Sroot 
7236002Sroot 	case TELOPT_BINARY:
7246002Sroot 		mode(RAW, 0);
72527188Sminshall 		fmt = will;
72627188Sminshall 		break;
7276002Sroot 
7286002Sroot 	case TELOPT_SGA:
7296002Sroot 		fmt = will;
7306002Sroot 		break;
7316002Sroot 
7326002Sroot 	default:
7336002Sroot 		fmt = wont;
7346002Sroot 		break;
7356002Sroot 	}
73627188Sminshall 	if (fmt == will) {
73727188Sminshall 	    myopts[option] = 1;
73827188Sminshall 	} else {
73927188Sminshall 	    myopts[option] = 0;
74027188Sminshall 	}
7416002Sroot 	sprintf(nfrontp, fmt, option);
7428379Ssam 	nfrontp += sizeof (doopt) - 2;
7436002Sroot }
7446002Sroot 
7456002Sroot mode(on, off)
7466002Sroot 	int on, off;
7476002Sroot {
7486002Sroot 	struct sgttyb b;
7496002Sroot 
7506002Sroot 	ptyflush();
7516002Sroot 	ioctl(pty, TIOCGETP, &b);
7526002Sroot 	b.sg_flags |= on;
7536002Sroot 	b.sg_flags &= ~off;
7546002Sroot 	ioctl(pty, TIOCSETP, &b);
7556002Sroot }
7566002Sroot 
7576002Sroot /*
7586002Sroot  * Send interrupt to process on other side of pty.
7596002Sroot  * If it is in raw mode, just write NULL;
7606002Sroot  * otherwise, write intr char.
7616002Sroot  */
7626002Sroot interrupt()
7636002Sroot {
7646002Sroot 	struct sgttyb b;
7656002Sroot 	struct tchars tchars;
7666002Sroot 
7676002Sroot 	ptyflush();	/* half-hearted */
7686002Sroot 	ioctl(pty, TIOCGETP, &b);
7696002Sroot 	if (b.sg_flags & RAW) {
7706002Sroot 		*pfrontp++ = '\0';
7716002Sroot 		return;
7726002Sroot 	}
7736002Sroot 	*pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ?
7746002Sroot 		'\177' : tchars.t_intrc;
7756002Sroot }
7766002Sroot 
77727229Sminshall /*
77827229Sminshall  * Send quit to process on other side of pty.
77927229Sminshall  * If it is in raw mode, just write NULL;
78027229Sminshall  * otherwise, write quit char.
78127229Sminshall  */
78227229Sminshall sendbrk()
78327229Sminshall {
78427229Sminshall 	struct sgttyb b;
78527229Sminshall 	struct tchars tchars;
78627229Sminshall 
78727229Sminshall 	ptyflush();	/* half-hearted */
78827229Sminshall 	ioctl(pty, TIOCGETP, &b);
78927229Sminshall 	if (b.sg_flags & RAW) {
79027229Sminshall 		*pfrontp++ = '\0';
79127229Sminshall 		return;
79227229Sminshall 	}
79327229Sminshall 	*pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ?
79427229Sminshall 		'\034' : tchars.t_quitc;
79527229Sminshall }
79627229Sminshall 
7976002Sroot ptyflush()
7986002Sroot {
7996002Sroot 	int n;
8006002Sroot 
8016002Sroot 	if ((n = pfrontp - pbackp) > 0)
8026002Sroot 		n = write(pty, pbackp, n);
8038346Ssam 	if (n < 0)
8048346Ssam 		return;
8056002Sroot 	pbackp += n;
8066002Sroot 	if (pbackp == pfrontp)
8076002Sroot 		pbackp = pfrontp = ptyobuf;
8086002Sroot }
809*27260Sminshall 
810*27260Sminshall /*
811*27260Sminshall  * nextitem()
812*27260Sminshall  *
813*27260Sminshall  *	Return the address of the next "item" in the TELNET data
814*27260Sminshall  * stream.  This will be the address of the next character if
815*27260Sminshall  * the current address is a user data character, or it will
816*27260Sminshall  * be the address of the character following the TELNET command
817*27260Sminshall  * if the current address is a TELNET IAC ("I Am a Command")
818*27260Sminshall  * character.
819*27260Sminshall  */
8206002Sroot 
821*27260Sminshall char *
822*27260Sminshall nextitem(current)
823*27260Sminshall char	*current;
8246002Sroot {
825*27260Sminshall     if ((*current&0xff) != IAC) {
826*27260Sminshall 	return current+1;
827*27260Sminshall     }
828*27260Sminshall     switch (*(current+1)&0xff) {
829*27260Sminshall     case DO:
830*27260Sminshall     case DONT:
831*27260Sminshall     case WILL:
832*27260Sminshall     case WONT:
833*27260Sminshall 	return current+3;
834*27260Sminshall     case SB:		/* loop forever looking for the SE */
835*27260Sminshall 	{
836*27260Sminshall 	    register char *look = current+2;
8376002Sroot 
838*27260Sminshall 	    for (;;) {
839*27260Sminshall 		if ((*look++&0xff) == IAC) {
840*27260Sminshall 		    if ((*look++&0xff) == SE) {
841*27260Sminshall 			return look;
842*27260Sminshall 		    }
843*27260Sminshall 		}
844*27260Sminshall 	    }
8458346Ssam 	}
846*27260Sminshall     default:
847*27260Sminshall 	return current+2;
848*27260Sminshall     }
8496002Sroot }
8506002Sroot 
85127185Sminshall 
85227185Sminshall /*
853*27260Sminshall  * netclear()
854*27260Sminshall  *
855*27260Sminshall  *	We are about to do a TELNET SYNCH operation.  Clear
856*27260Sminshall  * the path to the network.
857*27260Sminshall  *
858*27260Sminshall  *	Things are a bit tricky since we may have sent the first
859*27260Sminshall  * byte or so of a previous TELNET command into the network.
860*27260Sminshall  * So, we have to scan the network buffer from the beginning
861*27260Sminshall  * until we are up to where we want to be.
862*27260Sminshall  *
863*27260Sminshall  *	A side effect of what we do, just to keep things
864*27260Sminshall  * simple, is to clear the urgent data pointer.  The principal
865*27260Sminshall  * caller should be setting the urgent data pointer AFTER calling
866*27260Sminshall  * us in any case.
867*27260Sminshall  */
868*27260Sminshall 
869*27260Sminshall netclear()
870*27260Sminshall {
871*27260Sminshall     register char *thisitem, *next;
872*27260Sminshall     char *good;
873*27260Sminshall #define	wewant(p)	((nfrontp > p) && ((*p&0xff) == IAC) && \
874*27260Sminshall 				((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
875*27260Sminshall 
876*27260Sminshall     thisitem = netobuf;
877*27260Sminshall 
878*27260Sminshall     while ((next = nextitem(thisitem)) <= nbackp) {
879*27260Sminshall 	thisitem = next;
880*27260Sminshall     }
881*27260Sminshall 
882*27260Sminshall     /* Now, thisitem is first before/at boundary. */
883*27260Sminshall 
884*27260Sminshall     good = netobuf;	/* where the good bytes go */
885*27260Sminshall 
886*27260Sminshall     while (nfrontp > thisitem) {
887*27260Sminshall 	if (wewant(thisitem)) {
888*27260Sminshall 	    int length;
889*27260Sminshall 
890*27260Sminshall 	    next = thisitem;
891*27260Sminshall 	    do {
892*27260Sminshall 		next = nextitem(next);
893*27260Sminshall 	    } while (wewant(next) && (nfrontp > next));
894*27260Sminshall 	    length = next-thisitem;
895*27260Sminshall 	    bcopy(thisitem, good, length);
896*27260Sminshall 	    good += length;
897*27260Sminshall 	    thisitem = next;
898*27260Sminshall 	} else {
899*27260Sminshall 	    thisitem = nextitem(thisitem);
900*27260Sminshall 	}
901*27260Sminshall     }
902*27260Sminshall 
903*27260Sminshall     nbackp = netobuf;
904*27260Sminshall     nfrontp = good;		/* next byte to be sent */
905*27260Sminshall     neturg = 0;
906*27260Sminshall }
907*27260Sminshall 
908*27260Sminshall /*
90927185Sminshall  *  netflush
91027185Sminshall  *		Send as much data as possible to the network,
91127185Sminshall  *	handling requests for urgent data.
91227185Sminshall  */
91327185Sminshall 
91427185Sminshall 
91527185Sminshall netflush()
91627185Sminshall {
91727185Sminshall     int n;
91827185Sminshall 
91927185Sminshall     if ((n = nfrontp - nbackp) > 0) {
92027185Sminshall 	if (!neturg) {
92127185Sminshall 	    n = write(net, nbackp, n);	/* normal write */
92227185Sminshall 	} else {
92327185Sminshall 	    n = neturg - nbackp;
92427185Sminshall 	    /*
92527185Sminshall 	     * In 4.2 (and 4.3) systems, there is some question about
92627185Sminshall 	     * what byte in a sendOOB operation is the "OOB" data.
92727185Sminshall 	     * To make ourselves compatible, we only send ONE byte
92827185Sminshall 	     * out of band, the one WE THINK should be OOB (though
92927185Sminshall 	     * we really have more the TCP philosophy of urgent data
93027185Sminshall 	     * rather than the Unix philosophy of OOB data).
93127185Sminshall 	     */
93227185Sminshall 	    if (n > 1) {
93327185Sminshall 		n = send(net, nbackp, n-1, 0);	/* send URGENT all by itself */
93427185Sminshall 	    } else {
93527185Sminshall 		n = send(net, nbackp, n, MSG_OOB);	/* URGENT data */
93627185Sminshall 	    }
93727185Sminshall 	}
93827185Sminshall     }
93927185Sminshall     if (n < 0) {
94027185Sminshall 	if (errno == EWOULDBLOCK)
94127185Sminshall 	    return;
94227185Sminshall 	/* should blow this guy away... */
94327185Sminshall 	return;
94427185Sminshall     }
94527185Sminshall     nbackp += n;
94627185Sminshall     if (nbackp >= neturg) {
94727185Sminshall 	neturg = 0;
94827185Sminshall     }
94927185Sminshall     if (nbackp == nfrontp) {
95027185Sminshall 	nbackp = nfrontp = netobuf;
95127185Sminshall     }
95227185Sminshall }
95327185Sminshall 
9546002Sroot cleanup()
9556002Sroot {
9566002Sroot 
9576002Sroot 	rmut();
95810008Ssam 	vhangup();	/* XXX */
95910191Ssam 	shutdown(net, 2);
9606002Sroot 	exit(1);
9616002Sroot }
9626002Sroot 
9636002Sroot #include <utmp.h>
9646002Sroot 
9656002Sroot struct	utmp wtmp;
9666002Sroot char	wtmpf[]	= "/usr/adm/wtmp";
96723567Sbloom char	utmpf[] = "/etc/utmp";
96823567Sbloom #define SCPYN(a, b)	strncpy(a, b, sizeof(a))
96923567Sbloom #define SCMPN(a, b)	strncmp(a, b, sizeof(a))
9706002Sroot 
9716002Sroot rmut()
9726002Sroot {
9736002Sroot 	register f;
9746002Sroot 	int found = 0;
97523567Sbloom 	struct utmp *u, *utmp;
97623567Sbloom 	int nutmp;
97723567Sbloom 	struct stat statbf;
9786002Sroot 
97923567Sbloom 	f = open(utmpf, O_RDWR);
9806002Sroot 	if (f >= 0) {
98123567Sbloom 		fstat(f, &statbf);
98223567Sbloom 		utmp = (struct utmp *)malloc(statbf.st_size);
98323567Sbloom 		if (!utmp)
98423567Sbloom 			syslog(LOG_ERR, "utmp malloc failed");
98523567Sbloom 		if (statbf.st_size && utmp) {
98623567Sbloom 			nutmp = read(f, utmp, statbf.st_size);
98723567Sbloom 			nutmp /= sizeof(struct utmp);
98823567Sbloom 
98923567Sbloom 			for (u = utmp ; u < &utmp[nutmp] ; u++) {
99023567Sbloom 				if (SCMPN(u->ut_line, line+5) ||
99123567Sbloom 				    u->ut_name[0]==0)
99223567Sbloom 					continue;
99323567Sbloom 				lseek(f, ((long)u)-((long)utmp), L_SET);
99423567Sbloom 				SCPYN(u->ut_name, "");
99523567Sbloom 				SCPYN(u->ut_host, "");
99623567Sbloom 				time(&u->ut_time);
99723567Sbloom 				write(f, (char *)u, sizeof(wtmp));
99823567Sbloom 				found++;
99923567Sbloom 			}
10006002Sroot 		}
10016002Sroot 		close(f);
10026002Sroot 	}
10036002Sroot 	if (found) {
100417583Ssam 		f = open(wtmpf, O_WRONLY|O_APPEND);
10056002Sroot 		if (f >= 0) {
10066002Sroot 			SCPYN(wtmp.ut_line, line+5);
10076002Sroot 			SCPYN(wtmp.ut_name, "");
100812683Ssam 			SCPYN(wtmp.ut_host, "");
10096002Sroot 			time(&wtmp.ut_time);
101023567Sbloom 			write(f, (char *)&wtmp, sizeof(wtmp));
10116002Sroot 			close(f);
10126002Sroot 		}
10136002Sroot 	}
10146002Sroot 	chmod(line, 0666);
10156002Sroot 	chown(line, 0, 0);
10166002Sroot 	line[strlen("/dev/")] = 'p';
10176002Sroot 	chmod(line, 0666);
10186002Sroot 	chown(line, 0, 0);
10196002Sroot }
1020