xref: /csrg-svn/libexec/telnetd/telnetd.c (revision 37210)
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
634778Sbostic  * provided that the above copyright notice and this paragraph are
734778Sbostic  * duplicated in all such forms and that any documentation,
834778Sbostic  * advertising materials, and other materials related to such
934778Sbostic  * distribution and use acknowledge that the software was developed
1034778Sbostic  * by the University of California, Berkeley.  The name of the
1134778Sbostic  * University may not be used to endorse or promote products derived
1234778Sbostic  * from this software without specific prior written permission.
1334778Sbostic  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
1434778Sbostic  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
1534778Sbostic  * 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*37210Sminshall static char sccsid[] = "@(#)telnetd.c	5.32 (Berkeley) 03/18/89";
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 
125*37210Sminshall 	if ((argc > 1) && (strcmp(argv[1], "-debug") == 0)) {
12627185Sminshall 	    int s, ns, foo;
12727185Sminshall 	    struct servent *sp;
12827185Sminshall 	    static struct sockaddr_in sin = { AF_INET };
12927185Sminshall 
13027185Sminshall 	    argc--, argv++;
131*37210Sminshall 	    if (argc > 1) {
132*37210Sminshall 		    sin.sin_port = atoi(argv[1]);
13327185Sminshall 		    sin.sin_port = htons((u_short)sin.sin_port);
134*37210Sminshall 	    } else {
135*37210Sminshall 		sp = getservbyname("telnet", "tcp");
136*37210Sminshall 		if (sp == 0) {
137*37210Sminshall 			fprintf(stderr,
138*37210Sminshall 				"telnetd: tcp/telnet: unknown service\n");
139*37210Sminshall 			exit(1);
140*37210Sminshall 		}
141*37210Sminshall 		sin.sin_port = sp->s_port;
14227185Sminshall 	    }
14327185Sminshall 
14427185Sminshall 	    s = socket(AF_INET, SOCK_STREAM, 0);
14527185Sminshall 	    if (s < 0) {
14627185Sminshall 		    perror("telnetd: socket");;
14727185Sminshall 		    exit(1);
14827185Sminshall 	    }
14927185Sminshall 	    if (bind(s, &sin, sizeof sin) < 0) {
15027185Sminshall 		perror("bind");
15127185Sminshall 		exit(1);
15227185Sminshall 	    }
15327185Sminshall 	    if (listen(s, 1) < 0) {
15427185Sminshall 		perror("listen");
15527185Sminshall 		exit(1);
15627185Sminshall 	    }
15727185Sminshall 	    foo = sizeof sin;
15827185Sminshall 	    ns = accept(s, &sin, &foo);
15927185Sminshall 	    if (ns < 0) {
16027185Sminshall 		perror("accept");
16127185Sminshall 		exit(1);
16227185Sminshall 	    }
16327185Sminshall 	    dup2(ns, 0);
16427185Sminshall 	    close(s);
16527185Sminshall 	}
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 /*
215*37210Sminshall  * getterminalspeed
216*37210Sminshall  *
217*37210Sminshall  *     Ask the other end to send along its terminal speed.
218*37210Sminshall  * subopt does the rest.  Interlocked so it can't happen during
219*37210Sminshall  * getterminaltype.
220*37210Sminshall  */
221*37210Sminshall 
222*37210Sminshall void
223*37210Sminshall getterminalspeed()
224*37210Sminshall {
225*37210Sminshall   static char sbuf[] = { IAC, SB, TELOPT_TSPEED, TELQUAL_SEND, IAC, SE };
226*37210Sminshall 
227*37210Sminshall   bcopy(sbuf, nfrontp, sizeof sbuf);
228*37210Sminshall   nfrontp += sizeof sbuf;
229*37210Sminshall }
230*37210Sminshall 
231*37210Sminshall /*
23227983Sminshall  * getterminaltype
23327649Sminshall  *
23427983Sminshall  *	Ask the other end to send along its terminal type.
23527983Sminshall  * Output is the variable terminaltype filled in.
23627649Sminshall  */
23727649Sminshall 
23827983Sminshall void
23927983Sminshall getterminaltype()
24027649Sminshall {
24127983Sminshall     static char sbuf[] = { IAC, DO, TELOPT_TTYPE };
24227649Sminshall 
24327983Sminshall     settimer(getterminal);
24427983Sminshall     bcopy(sbuf, nfrontp, sizeof sbuf);
24527983Sminshall     nfrontp += sizeof sbuf;
24628044Sminshall     hisopts[TELOPT_TTYPE] = OPT_YES_BUT_ALWAYS_LOOK;
24727983Sminshall     while (sequenceIs(ttypeopt, getterminal)) {
24827983Sminshall 	ttloop();
24927649Sminshall     }
25027983Sminshall     if (hisopts[TELOPT_TTYPE] == OPT_YES) {
25127983Sminshall 	static char sbbuf[] = { IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE };
25227983Sminshall 
25327983Sminshall 	bcopy(sbbuf, nfrontp, sizeof sbbuf);
25427983Sminshall 	nfrontp += sizeof sbbuf;
25527983Sminshall 	while (sequenceIs(ttypesubopt, getterminal)) {
25627983Sminshall 	    ttloop();
25727983Sminshall 	}
25827983Sminshall     }
25927649Sminshall }
26027649Sminshall 
2616002Sroot /*
2626002Sroot  * Get a pty, scan input lines.
2636002Sroot  */
26412683Ssam doit(f, who)
26512683Ssam 	int f;
26612683Ssam 	struct sockaddr_in *who;
2676002Sroot {
26820188Skarels 	char *host, *inet_ntoa();
26917583Ssam 	int i, p, t;
2706002Sroot 	struct sgttyb b;
27112683Ssam 	struct hostent *hp;
27227649Sminshall 	int c;
2736002Sroot 
27420188Skarels 	for (c = 'p'; c <= 's'; c++) {
27520188Skarels 		struct stat stb;
27620188Skarels 
27720188Skarels 		line = "/dev/ptyXX";
27820188Skarels 		line[strlen("/dev/pty")] = c;
27920188Skarels 		line[strlen("/dev/ptyp")] = '0';
28020188Skarels 		if (stat(line, &stb) < 0)
28120188Skarels 			break;
28217583Ssam 		for (i = 0; i < 16; i++) {
28334424Sbostic 			line[sizeof("/dev/ptyp") - 1] = "0123456789abcdef"[i];
28434424Sbostic 			p = open(line, O_RDWR);
28517583Ssam 			if (p > 0)
28617583Ssam 				goto gotpty;
28717583Ssam 		}
2886002Sroot 	}
2899244Ssam 	fatal(f, "All network ports in use");
2909244Ssam 	/*NOTREACHED*/
2916002Sroot gotpty:
2926002Sroot 	dup2(f, 0);
29320188Skarels 	line[strlen("/dev/")] = 't';
29417583Ssam 	t = open("/dev/tty", O_RDWR);
2956002Sroot 	if (t >= 0) {
2966002Sroot 		ioctl(t, TIOCNOTTY, 0);
2976002Sroot 		close(t);
2986002Sroot 	}
29920188Skarels 	t = open(line, O_RDWR);
3009244Ssam 	if (t < 0)
30134424Sbostic 		fatalperror(f, line);
30234424Sbostic 	if (fchmod(t, 0))
30334424Sbostic 		fatalperror(f, line);
30434424Sbostic 	(void)signal(SIGHUP, SIG_IGN);
30534424Sbostic 	vhangup();
30634424Sbostic 	(void)signal(SIGHUP, SIG_DFL);
30734424Sbostic 	t = open(line, O_RDWR);
30834424Sbostic 	if (t < 0)
30934424Sbostic 		fatalperror(f, line);
3106002Sroot 	ioctl(t, TIOCGETP, &b);
3116388Ssam 	b.sg_flags = CRMOD|XTABS|ANYP;
3126002Sroot 	ioctl(t, TIOCSETP, &b);
3136388Ssam 	ioctl(p, TIOCGETP, &b);
3148379Ssam 	b.sg_flags &= ~ECHO;
315*37210Sminshall 	b.sg_ospeed = b.sg_ispeed = B9600;
3166388Ssam 	ioctl(p, TIOCSETP, &b);
31712683Ssam 	hp = gethostbyaddr(&who->sin_addr, sizeof (struct in_addr),
31812683Ssam 		who->sin_family);
31912683Ssam 	if (hp)
32012683Ssam 		host = hp->h_name;
32112683Ssam 	else
32217444Sralph 		host = inet_ntoa(who->sin_addr);
32327983Sminshall 
32427983Sminshall 	net = f;
32527983Sminshall 	pty = p;
32627983Sminshall 
32727983Sminshall 	/*
328*37210Sminshall 	 * get terminal type and size.
32927983Sminshall 	 */
33027983Sminshall 	getterminaltype();
33127983Sminshall 
3329244Ssam 	if ((i = fork()) < 0)
33334424Sbostic 		fatalperror(f, "fork");
3346002Sroot 	if (i)
3356002Sroot 		telnet(f, p);
3366002Sroot 	close(f);
3376002Sroot 	close(p);
3386002Sroot 	dup2(t, 0);
3396002Sroot 	dup2(t, 1);
3406002Sroot 	dup2(t, 2);
3416002Sroot 	close(t);
34227983Sminshall 	envinit[0] = terminaltype;
34327983Sminshall 	envinit[1] = 0;
34413799Ssam 	environ = envinit;
34527649Sminshall 	/*
34627649Sminshall 	 * -h : pass on name of host.
34727983Sminshall 	 *		WARNING:  -h is accepted by login if and only if
34827983Sminshall 	 *			getuid() == 0.
34927649Sminshall 	 * -p : don't clobber the environment (so terminal type stays set).
35027649Sminshall 	 */
35127649Sminshall 	execl("/bin/login", "login", "-h", host,
35227983Sminshall 					terminaltype ? "-p" : 0, 0);
35336878Skarels 	syslog(LOG_ERR, "/bin/login: %m\n");
35436878Skarels 	fatalperror(2, "/bin/login");
3559244Ssam 	/*NOTREACHED*/
3569244Ssam }
3579244Ssam 
3589244Ssam fatal(f, msg)
3599244Ssam 	int f;
3609244Ssam 	char *msg;
3619244Ssam {
3629244Ssam 	char buf[BUFSIZ];
3639244Ssam 
36417583Ssam 	(void) sprintf(buf, "telnetd: %s.\r\n", msg);
3659244Ssam 	(void) write(f, buf, strlen(buf));
3666002Sroot 	exit(1);
3676002Sroot }
3686002Sroot 
36934424Sbostic fatalperror(f, msg)
3709244Ssam 	int f;
3719244Ssam 	char *msg;
3729244Ssam {
3739244Ssam 	char buf[BUFSIZ];
3749244Ssam 	extern char *sys_errlist[];
3759244Ssam 
37617583Ssam 	(void) sprintf(buf, "%s: %s\r\n", msg, sys_errlist[errno]);
3779244Ssam 	fatal(f, buf);
3789244Ssam }
3799244Ssam 
38027185Sminshall 
3816002Sroot /*
38227185Sminshall  * Check a descriptor to see if out of band data exists on it.
38327185Sminshall  */
38427185Sminshall 
38527185Sminshall 
38627185Sminshall stilloob(s)
38727185Sminshall int	s;		/* socket number */
38827185Sminshall {
38927185Sminshall     static struct timeval timeout = { 0 };
39027185Sminshall     fd_set	excepts;
39127185Sminshall     int value;
39227185Sminshall 
39327185Sminshall     do {
39427185Sminshall 	FD_ZERO(&excepts);
39527185Sminshall 	FD_SET(s, &excepts);
39627185Sminshall 	value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout);
39727898Skarels     } while ((value == -1) && (errno == EINTR));
39827185Sminshall 
39927185Sminshall     if (value < 0) {
40034424Sbostic 	fatalperror(pty, "select");
40127185Sminshall     }
40227185Sminshall     if (FD_ISSET(s, &excepts)) {
40327185Sminshall 	return 1;
40427185Sminshall     } else {
40527185Sminshall 	return 0;
40627185Sminshall     }
40727185Sminshall }
40827185Sminshall 
40927185Sminshall /*
4106002Sroot  * Main loop.  Select from pty and network, and
4116002Sroot  * hand data to telnet receiver finite state machine.
4126002Sroot  */
4136002Sroot telnet(f, p)
4146002Sroot {
4156002Sroot 	int on = 1;
41627898Skarels 	char hostname[MAXHOSTNAMELEN];
41733271Sminshall #define	TABBUFSIZ	512
41833271Sminshall 	char	defent[TABBUFSIZ];
41933271Sminshall 	char	defstrs[TABBUFSIZ];
42033271Sminshall #undef	TABBUFSIZ
42133271Sminshall 	char *HE;
42233271Sminshall 	char *HN;
42333271Sminshall 	char *IM;
4246002Sroot 
4256002Sroot 	ioctl(f, FIONBIO, &on);
4266002Sroot 	ioctl(p, FIONBIO, &on);
42733267Sminshall 	ioctl(p, TIOCPKT, &on);
42827649Sminshall #if	defined(SO_OOBINLINE)
42927649Sminshall 	setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on);
43027649Sminshall #endif	/* defined(SO_OOBINLINE) */
4316002Sroot 	signal(SIGTSTP, SIG_IGN);
43232400Sminshall 	/*
43332400Sminshall 	 * Ignoring SIGTTOU keeps the kernel from blocking us
43432400Sminshall 	 * in ttioctl() in /sys/tty.c.
43532400Sminshall 	 */
43632400Sminshall 	signal(SIGTTOU, SIG_IGN);
43713028Ssam 	signal(SIGCHLD, cleanup);
43826083Slepreau 	setpgrp(0, 0);
4396002Sroot 
4408379Ssam 	/*
44127185Sminshall 	 * Request to do remote echo and to suppress go ahead.
4428379Ssam 	 */
44327983Sminshall 	if (!myopts[TELOPT_ECHO]) {
44427983Sminshall 	    dooption(TELOPT_ECHO);
44527983Sminshall 	}
44627983Sminshall 	if (!myopts[TELOPT_SGA]) {
44727983Sminshall 	    dooption(TELOPT_SGA);
44827983Sminshall 	}
449*37210Sminshall 	if (!hisopts[TELOPT_NAWS]) {
450*37210Sminshall 	   willoption(TELOPT_NAWS);
451*37210Sminshall 	   hisopts[TELOPT_NAWS] = OPT_NO;
452*37210Sminshall 	}
453*37210Sminshall 	if (!hisopts[TELOPT_TSPEED]) {
454*37210Sminshall 	   willoption(TELOPT_TSPEED);
455*37210Sminshall 	   hisopts[TELOPT_TSPEED] = OPT_NO;
456*37210Sminshall 	}
457*37210Sminshall 	if (!hisopts[TELOPT_LFLOW]) {
458*37210Sminshall 	   willoption(TELOPT_LFLOW);
459*37210Sminshall 	   hisopts[TELOPT_LFLOW] = OPT_NO;
460*37210Sminshall 	}
461*37210Sminshall 
46212713Ssam 	/*
46327649Sminshall 	 * Is the client side a 4.2 (NOT 4.3) system?  We need to know this
46427649Sminshall 	 * because 4.2 clients are unable to deal with TCP urgent data.
46527649Sminshall 	 *
46627649Sminshall 	 * To find out, we send out a "DO ECHO".  If the remote system
46727649Sminshall 	 * answers "WILL ECHO" it is probably a 4.2 client, and we note
46827649Sminshall 	 * that fact ("WILL ECHO" ==> that the client will echo what
46927649Sminshall 	 * WE, the server, sends it; it does NOT mean that the client will
47027649Sminshall 	 * echo the terminal input).
47127649Sminshall 	 */
47232452Sbostic 	(void) sprintf(nfrontp, doopt, TELOPT_ECHO);
47327649Sminshall 	nfrontp += sizeof doopt-2;
47427983Sminshall 	hisopts[TELOPT_ECHO] = OPT_YES_BUT_ALWAYS_LOOK;
47527649Sminshall 
47627649Sminshall 	/*
47712713Ssam 	 * Show banner that getty never gave.
47827797Sminshall 	 *
47933271Sminshall 	 * We put the banner in the pty input buffer.  This way, it
48033271Sminshall 	 * gets carriage return null processing, etc., just like all
48133271Sminshall 	 * other pty --> client data.
48212713Ssam 	 */
48327797Sminshall 
48412713Ssam 	gethostname(hostname, sizeof (hostname));
48533271Sminshall 	if (getent(defent, "default") == 1) {
48633271Sminshall 		char *getstr();
48733271Sminshall 		char *p=defstrs;
48827649Sminshall 
48933271Sminshall 		HE = getstr("he", &p);
49033271Sminshall 		HN = getstr("hn", &p);
49133271Sminshall 		IM = getstr("im", &p);
49233271Sminshall 		if (HN && *HN)
49333271Sminshall 			strcpy(hostname, HN);
49433271Sminshall 		edithost(HE, hostname);
49533271Sminshall 		if (IM && *IM)
49634424Sbostic 			putf(IM, ptyibuf+1);
49733271Sminshall 	} else {
49833271Sminshall 		sprintf(ptyibuf+1, BANNER, hostname);
49933271Sminshall 	}
50027797Sminshall 
50133271Sminshall 	ptyip = ptyibuf+1;		/* Prime the pump */
50233271Sminshall 	pcc = strlen(ptyip);		/* ditto */
50333271Sminshall 
50433267Sminshall 	/* Clear ptybuf[0] - where the packet information is received */
50533267Sminshall 	ptyibuf[0] = 0;
50633271Sminshall 
50727649Sminshall 	/*
50827649Sminshall 	 * Call telrcv() once to pick up anything received during
50927649Sminshall 	 * terminal type negotiation.
51027649Sminshall 	 */
51127649Sminshall 	telrcv();
51227649Sminshall 
5136002Sroot 	for (;;) {
51427185Sminshall 		fd_set ibits, obits, xbits;
5156002Sroot 		register int c;
5166002Sroot 
51727185Sminshall 		if (ncc < 0 && pcc < 0)
51827185Sminshall 			break;
51927185Sminshall 
52027185Sminshall 		FD_ZERO(&ibits);
52127185Sminshall 		FD_ZERO(&obits);
52227185Sminshall 		FD_ZERO(&xbits);
5236002Sroot 		/*
5246002Sroot 		 * Never look for input if there's still
5256002Sroot 		 * stuff in the corresponding output buffer
5266002Sroot 		 */
52727185Sminshall 		if (nfrontp - nbackp || pcc > 0) {
52827185Sminshall 			FD_SET(f, &obits);
52927185Sminshall 		} else {
53027185Sminshall 			FD_SET(p, &ibits);
53127185Sminshall 		}
53227185Sminshall 		if (pfrontp - pbackp || ncc > 0) {
53327185Sminshall 			FD_SET(p, &obits);
53427185Sminshall 		} else {
53527185Sminshall 			FD_SET(f, &ibits);
53627185Sminshall 		}
53727185Sminshall 		if (!SYNCHing) {
53827185Sminshall 			FD_SET(f, &xbits);
53927185Sminshall 		}
540*37210Sminshall 		FD_SET(p, &xbits);
54127185Sminshall 		if ((c = select(16, &ibits, &obits, &xbits,
54227185Sminshall 						(struct timeval *)0)) < 1) {
54327185Sminshall 			if (c == -1) {
54427185Sminshall 				if (errno == EINTR) {
54527185Sminshall 					continue;
54627185Sminshall 				}
54727185Sminshall 			}
5486002Sroot 			sleep(5);
5496002Sroot 			continue;
5506002Sroot 		}
5516002Sroot 
5526002Sroot 		/*
55327185Sminshall 		 * Any urgent data?
55427185Sminshall 		 */
55527185Sminshall 		if (FD_ISSET(net, &xbits)) {
55627185Sminshall 		    SYNCHing = 1;
55727185Sminshall 		}
55827185Sminshall 
55927185Sminshall 		/*
5606002Sroot 		 * Something to read from the network...
5616002Sroot 		 */
56227185Sminshall 		if (FD_ISSET(net, &ibits)) {
56327649Sminshall #if	!defined(SO_OOBINLINE)
56427185Sminshall 			/*
56527898Skarels 			 * In 4.2 (and 4.3 beta) systems, the
56627185Sminshall 			 * OOB indication and data handling in the kernel
56727185Sminshall 			 * is such that if two separate TCP Urgent requests
56827185Sminshall 			 * come in, one byte of TCP data will be overlaid.
56927185Sminshall 			 * This is fatal for Telnet, but we try to live
57027185Sminshall 			 * with it.
57127185Sminshall 			 *
57227185Sminshall 			 * In addition, in 4.2 (and...), a special protocol
57327185Sminshall 			 * is needed to pick up the TCP Urgent data in
57427185Sminshall 			 * the correct sequence.
57527185Sminshall 			 *
57627185Sminshall 			 * What we do is:  if we think we are in urgent
57727185Sminshall 			 * mode, we look to see if we are "at the mark".
57827185Sminshall 			 * If we are, we do an OOB receive.  If we run
57927185Sminshall 			 * this twice, we will do the OOB receive twice,
58027185Sminshall 			 * but the second will fail, since the second
58127185Sminshall 			 * time we were "at the mark", but there wasn't
58227185Sminshall 			 * any data there (the kernel doesn't reset
58327185Sminshall 			 * "at the mark" until we do a normal read).
58427185Sminshall 			 * Once we've read the OOB data, we go ahead
58527185Sminshall 			 * and do normal reads.
58627185Sminshall 			 *
58727185Sminshall 			 * There is also another problem, which is that
58827185Sminshall 			 * since the OOB byte we read doesn't put us
58927185Sminshall 			 * out of OOB state, and since that byte is most
59027185Sminshall 			 * likely the TELNET DM (data mark), we would
59127185Sminshall 			 * stay in the TELNET SYNCH (SYNCHing) state.
59227185Sminshall 			 * So, clocks to the rescue.  If we've "just"
59327185Sminshall 			 * received a DM, then we test for the
59427185Sminshall 			 * presence of OOB data when the receive OOB
59527185Sminshall 			 * fails (and AFTER we did the normal mode read
59627185Sminshall 			 * to clear "at the mark").
59727185Sminshall 			 */
59827185Sminshall 		    if (SYNCHing) {
59927185Sminshall 			int atmark;
60027185Sminshall 
60127185Sminshall 			ioctl(net, SIOCATMARK, (char *)&atmark);
60227185Sminshall 			if (atmark) {
60327185Sminshall 			    ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB);
60427185Sminshall 			    if ((ncc == -1) && (errno == EINVAL)) {
60527185Sminshall 				ncc = read(net, netibuf, sizeof (netibuf));
60627983Sminshall 				if (sequenceIs(didnetreceive, gotDM)) {
60727185Sminshall 				    SYNCHing = stilloob(net);
60827185Sminshall 				}
60927185Sminshall 			    }
61027185Sminshall 			} else {
61127185Sminshall 			    ncc = read(net, netibuf, sizeof (netibuf));
6126002Sroot 			}
61327185Sminshall 		    } else {
61427185Sminshall 			ncc = read(net, netibuf, sizeof (netibuf));
61527185Sminshall 		    }
61627185Sminshall 		    settimer(didnetreceive);
61727649Sminshall #else	/* !defined(SO_OOBINLINE)) */
61827185Sminshall 		    ncc = read(net, netibuf, sizeof (netibuf));
61927649Sminshall #endif	/* !defined(SO_OOBINLINE)) */
62027185Sminshall 		    if (ncc < 0 && errno == EWOULDBLOCK)
62127185Sminshall 			ncc = 0;
62227185Sminshall 		    else {
62327185Sminshall 			if (ncc <= 0) {
62427185Sminshall 			    break;
62527185Sminshall 			}
62627185Sminshall 			netip = netibuf;
62727185Sminshall 		    }
6286002Sroot 		}
6296002Sroot 
6306002Sroot 		/*
6316002Sroot 		 * Something to read from the pty...
6326002Sroot 		 */
633*37210Sminshall 		if (FD_ISSET(p, &ibits) || FD_ISSET(p, &xbits)) {
6346002Sroot 			pcc = read(p, ptyibuf, BUFSIZ);
6356002Sroot 			if (pcc < 0 && errno == EWOULDBLOCK)
6366002Sroot 				pcc = 0;
6376002Sroot 			else {
6386002Sroot 				if (pcc <= 0)
6396002Sroot 					break;
640*37210Sminshall 				if (ptyibuf[0] & TIOCPKT_FLUSHWRITE) {
641*37210Sminshall 					netclear();     /* clear buffer back */
642*37210Sminshall 					*nfrontp++ = IAC;
643*37210Sminshall 					*nfrontp++ = DM;
644*37210Sminshall 					neturg = nfrontp-1; /* off by one XXX */
645*37210Sminshall 				}
646*37210Sminshall 				if (hisopts[TELOPT_LFLOW] &&
647*37210Sminshall 				    (ptyibuf[0] &
648*37210Sminshall 				      (TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))) {
649*37210Sminshall 					sprintf(nfrontp,"%c%c%c%c%c%c",
650*37210Sminshall 					    IAC, SB, TELOPT_LFLOW,
651*37210Sminshall 					    ptyibuf[0] & TIOCPKT_DOSTOP ? 1 : 0,
652*37210Sminshall 					    IAC, SE);
653*37210Sminshall 					nfrontp += 6;
654*37210Sminshall 				}
65533267Sminshall 				pcc--;
65633267Sminshall 				ptyip = ptyibuf+1;
657*37210Sminshall                        }
6586002Sroot 		}
6596002Sroot 
6606002Sroot 		while (pcc > 0) {
6616002Sroot 			if ((&netobuf[BUFSIZ] - nfrontp) < 2)
6626002Sroot 				break;
6636002Sroot 			c = *ptyip++ & 0377, pcc--;
6646002Sroot 			if (c == IAC)
6656002Sroot 				*nfrontp++ = c;
6666002Sroot 			*nfrontp++ = c;
66731940Sbostic 			/* Don't do CR-NUL if we are in binary mode */
66831940Sbostic 			if ((c == '\r') && (myopts[TELOPT_BINARY] == OPT_NO)) {
66927020Sminshall 				if (pcc > 0 && ((*ptyip & 0377) == '\n')) {
67027020Sminshall 					*nfrontp++ = *ptyip++ & 0377;
67127020Sminshall 					pcc--;
67227020Sminshall 				} else
67327020Sminshall 					*nfrontp++ = '\0';
67427020Sminshall 			}
6756002Sroot 		}
67627185Sminshall 		if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0)
6776002Sroot 			netflush();
6786002Sroot 		if (ncc > 0)
6796002Sroot 			telrcv();
68027185Sminshall 		if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0)
6816002Sroot 			ptyflush();
6826002Sroot 	}
6836002Sroot 	cleanup();
6846002Sroot }
6856002Sroot 
6866002Sroot /*
6876002Sroot  * State for recv fsm
6886002Sroot  */
6896002Sroot #define	TS_DATA		0	/* base state */
6906002Sroot #define	TS_IAC		1	/* look for double IAC's */
6916002Sroot #define	TS_CR		2	/* CR-LF ->'s CR */
69227649Sminshall #define	TS_SB		3	/* throw away begin's... */
69327649Sminshall #define	TS_SE		4	/* ...end's (suboption negotiation) */
6946002Sroot #define	TS_WILL		5	/* will option negotiation */
6956002Sroot #define	TS_WONT		6	/* wont " */
6966002Sroot #define	TS_DO		7	/* do " */
6976002Sroot #define	TS_DONT		8	/* dont " */
6986002Sroot 
6996002Sroot telrcv()
7006002Sroot {
7016002Sroot 	register int c;
7026002Sroot 	static int state = TS_DATA;
7036002Sroot 
7046002Sroot 	while (ncc > 0) {
7056002Sroot 		if ((&ptyobuf[BUFSIZ] - pfrontp) < 2)
7066002Sroot 			return;
7076002Sroot 		c = *netip++ & 0377, ncc--;
7086002Sroot 		switch (state) {
7096002Sroot 
71026090Sminshall 		case TS_CR:
71126090Sminshall 			state = TS_DATA;
71232097Sminshall 			/* Strip off \n or \0 after a \r */
71326499Sminshall 			if ((c == 0) || (c == '\n')) {
71426090Sminshall 				break;
71526499Sminshall 			}
71626090Sminshall 			/* FALL THROUGH */
71726090Sminshall 
7186002Sroot 		case TS_DATA:
7196002Sroot 			if (c == IAC) {
7206002Sroot 				state = TS_IAC;
7216002Sroot 				break;
7226002Sroot 			}
7236002Sroot 			if (inter > 0)
7246002Sroot 				break;
72527020Sminshall 			/*
72632097Sminshall 			 * We now map \r\n ==> \r for pragmatic reasons.
72732097Sminshall 			 * Many client implementations send \r\n when
72832097Sminshall 			 * the user hits the CarriageReturn key.
72932097Sminshall 			 *
73032097Sminshall 			 * We USED to map \r\n ==> \n, since \r\n says
73127020Sminshall 			 * that we want to be in column 1 of the next
73227020Sminshall 			 * printable line, and \n is the standard
73327020Sminshall 			 * unix way of saying that (\r is only good
73427020Sminshall 			 * if CRMOD is set, which it normally is).
73527020Sminshall 			 */
73631940Sbostic 			if ((c == '\r') && (hisopts[TELOPT_BINARY] == OPT_NO)) {
73732097Sminshall 				state = TS_CR;
73826499Sminshall 			}
73926499Sminshall 			*pfrontp++ = c;
7406002Sroot 			break;
7416002Sroot 
7426002Sroot 		case TS_IAC:
7436002Sroot 			switch (c) {
7446002Sroot 
7456002Sroot 			/*
7466002Sroot 			 * Send the process on the pty side an
7476002Sroot 			 * interrupt.  Do this with a NULL or
7486002Sroot 			 * interrupt char; depending on the tty mode.
7496002Sroot 			 */
7506002Sroot 			case IP:
7516002Sroot 				interrupt();
7526002Sroot 				break;
7536002Sroot 
75427229Sminshall 			case BREAK:
75527229Sminshall 				sendbrk();
75627229Sminshall 				break;
75727229Sminshall 
7586002Sroot 			/*
7596002Sroot 			 * Are You There?
7606002Sroot 			 */
7616002Sroot 			case AYT:
76217583Ssam 				strcpy(nfrontp, "\r\n[Yes]\r\n");
76317583Ssam 				nfrontp += 9;
7646002Sroot 				break;
7656002Sroot 
7666002Sroot 			/*
76727185Sminshall 			 * Abort Output
76827185Sminshall 			 */
76927185Sminshall 			case AO: {
77027185Sminshall 					struct ltchars tmpltc;
77127185Sminshall 
77227185Sminshall 					ptyflush();	/* half-hearted */
77327185Sminshall 					ioctl(pty, TIOCGLTC, &tmpltc);
77427185Sminshall 					if (tmpltc.t_flushc != '\377') {
77527185Sminshall 						*pfrontp++ = tmpltc.t_flushc;
77627185Sminshall 					}
77727260Sminshall 					netclear();	/* clear buffer back */
77827185Sminshall 					*nfrontp++ = IAC;
77927185Sminshall 					*nfrontp++ = DM;
78027187Sminshall 					neturg = nfrontp-1; /* off by one XXX */
78127185Sminshall 					break;
78227185Sminshall 				}
78327185Sminshall 
78427185Sminshall 			/*
7856002Sroot 			 * Erase Character and
7866002Sroot 			 * Erase Line
7876002Sroot 			 */
7886002Sroot 			case EC:
78927185Sminshall 			case EL: {
79027185Sminshall 					struct sgttyb b;
79127185Sminshall 					char ch;
7926002Sroot 
79327185Sminshall 					ptyflush();	/* half-hearted */
79427185Sminshall 					ioctl(pty, TIOCGETP, &b);
79527185Sminshall 					ch = (c == EC) ?
79627185Sminshall 						b.sg_erase : b.sg_kill;
79727185Sminshall 					if (ch != '\377') {
79827185Sminshall 						*pfrontp++ = ch;
79927185Sminshall 					}
80027185Sminshall 					break;
80127185Sminshall 				}
80227185Sminshall 
8036002Sroot 			/*
8046002Sroot 			 * Check for urgent data...
8056002Sroot 			 */
8066002Sroot 			case DM:
80727185Sminshall 				SYNCHing = stilloob(net);
80827185Sminshall 				settimer(gotDM);
8096002Sroot 				break;
8106002Sroot 
81127185Sminshall 
8126002Sroot 			/*
8136002Sroot 			 * Begin option subnegotiation...
8146002Sroot 			 */
8156002Sroot 			case SB:
81627649Sminshall 				state = TS_SB;
817*37210Sminshall 				SB_CLEAR();
8186002Sroot 				continue;
8196002Sroot 
8206002Sroot 			case WILL:
82127188Sminshall 				state = TS_WILL;
82227188Sminshall 				continue;
82327188Sminshall 
8246002Sroot 			case WONT:
82527188Sminshall 				state = TS_WONT;
82627188Sminshall 				continue;
82727188Sminshall 
8286002Sroot 			case DO:
82927188Sminshall 				state = TS_DO;
83027188Sminshall 				continue;
83127188Sminshall 
8326002Sroot 			case DONT:
83327188Sminshall 				state = TS_DONT;
8346002Sroot 				continue;
8356002Sroot 
8366002Sroot 			case IAC:
8376002Sroot 				*pfrontp++ = c;
8386002Sroot 				break;
8396002Sroot 			}
8406002Sroot 			state = TS_DATA;
8416002Sroot 			break;
8426002Sroot 
84327649Sminshall 		case TS_SB:
84427649Sminshall 			if (c == IAC) {
84527649Sminshall 				state = TS_SE;
84627649Sminshall 			} else {
84727649Sminshall 				SB_ACCUM(c);
84827649Sminshall 			}
8496002Sroot 			break;
8506002Sroot 
85127649Sminshall 		case TS_SE:
85227649Sminshall 			if (c != SE) {
85327649Sminshall 				if (c != IAC) {
85427649Sminshall 					SB_ACCUM(IAC);
85527649Sminshall 				}
85627649Sminshall 				SB_ACCUM(c);
85727649Sminshall 				state = TS_SB;
85827649Sminshall 			} else {
85927649Sminshall 				SB_TERM();
86027649Sminshall 				suboption();	/* handle sub-option */
86127649Sminshall 				state = TS_DATA;
86227649Sminshall 			}
8636002Sroot 			break;
8646002Sroot 
8656002Sroot 		case TS_WILL:
86627983Sminshall 			if (hisopts[c] != OPT_YES)
8676002Sroot 				willoption(c);
8686002Sroot 			state = TS_DATA;
869*37210Sminshall 			if (c == TELOPT_TSPEED)
870*37210Sminshall 				getterminalspeed();
8716002Sroot 			continue;
8726002Sroot 
8736002Sroot 		case TS_WONT:
87427983Sminshall 			if (hisopts[c] != OPT_NO)
8756002Sroot 				wontoption(c);
8766002Sroot 			state = TS_DATA;
8776002Sroot 			continue;
8786002Sroot 
8796002Sroot 		case TS_DO:
88027983Sminshall 			if (myopts[c] != OPT_YES)
8816002Sroot 				dooption(c);
8826002Sroot 			state = TS_DATA;
8836002Sroot 			continue;
8846002Sroot 
8856002Sroot 		case TS_DONT:
88627983Sminshall 			if (myopts[c] != OPT_NO) {
88727649Sminshall 				dontoption(c);
8886002Sroot 			}
8896002Sroot 			state = TS_DATA;
8906002Sroot 			continue;
8916002Sroot 
8926002Sroot 		default:
89327898Skarels 			syslog(LOG_ERR, "telnetd: panic state=%d\n", state);
8949218Ssam 			printf("telnetd: panic state=%d\n", state);
8956002Sroot 			exit(1);
8966002Sroot 		}
8976002Sroot 	}
8986002Sroot }
8996002Sroot 
9006002Sroot willoption(option)
9016002Sroot 	int option;
9026002Sroot {
9036002Sroot 	char *fmt;
9046002Sroot 
9056002Sroot 	switch (option) {
9066002Sroot 
9076002Sroot 	case TELOPT_BINARY:
9086002Sroot 		mode(RAW, 0);
90927188Sminshall 		fmt = doopt;
91027188Sminshall 		break;
9116002Sroot 
9126002Sroot 	case TELOPT_ECHO:
91327649Sminshall 		not42 = 0;		/* looks like a 4.2 system */
91427649Sminshall 		/*
91527649Sminshall 		 * Now, in a 4.2 system, to break them out of ECHOing
91627649Sminshall 		 * (to the terminal) mode, we need to send a "WILL ECHO".
91727649Sminshall 		 * Kludge upon kludge!
91827649Sminshall 		 */
91927983Sminshall 		if (myopts[TELOPT_ECHO] == OPT_YES) {
92027649Sminshall 		    dooption(TELOPT_ECHO);
92127649Sminshall 		}
92227649Sminshall 		fmt = dont;
92327188Sminshall 		break;
9246002Sroot 
92527649Sminshall 	case TELOPT_TTYPE:
92627983Sminshall 		settimer(ttypeopt);
92727983Sminshall 		if (hisopts[TELOPT_TTYPE] == OPT_YES_BUT_ALWAYS_LOOK) {
92827983Sminshall 		    hisopts[TELOPT_TTYPE] = OPT_YES;
92927983Sminshall 		    return;
93027983Sminshall 		}
93127983Sminshall 		fmt = doopt;
93227983Sminshall 		break;
93327983Sminshall 
934*37210Sminshall 	case TELOPT_NAWS:
935*37210Sminshall 	case TELOPT_TSPEED:
936*37210Sminshall 	case TELOPT_LFLOW:
9376002Sroot 	case TELOPT_SGA:
9386002Sroot 		fmt = doopt;
9396002Sroot 		break;
9406002Sroot 
9416002Sroot 	case TELOPT_TM:
9426002Sroot 		fmt = dont;
9436002Sroot 		break;
9446002Sroot 
9456002Sroot 	default:
9466002Sroot 		fmt = dont;
9476002Sroot 		break;
9486002Sroot 	}
94927188Sminshall 	if (fmt == doopt) {
95027983Sminshall 		hisopts[option] = OPT_YES;
95127188Sminshall 	} else {
95227983Sminshall 		hisopts[option] = OPT_NO;
95327188Sminshall 	}
95432452Sbostic 	(void) sprintf(nfrontp, fmt, option);
9558379Ssam 	nfrontp += sizeof (dont) - 2;
9566002Sroot }
9576002Sroot 
9586002Sroot wontoption(option)
9596002Sroot 	int option;
9606002Sroot {
9616002Sroot 	char *fmt;
9626002Sroot 
9636002Sroot 	switch (option) {
9646002Sroot 	case TELOPT_ECHO:
96527649Sminshall 		not42 = 1;		/* doesn't seem to be a 4.2 system */
96627188Sminshall 		break;
9676002Sroot 
9686002Sroot 	case TELOPT_BINARY:
9696002Sroot 		mode(0, RAW);
9706002Sroot 		break;
97128044Sminshall 
97228044Sminshall 	case TELOPT_TTYPE:
97328044Sminshall 	    settimer(ttypeopt);
97428044Sminshall 	    break;
9756002Sroot 	}
97628044Sminshall 
97727188Sminshall 	fmt = dont;
97827983Sminshall 	hisopts[option] = OPT_NO;
97932452Sbostic 	(void) sprintf(nfrontp, fmt, option);
9808379Ssam 	nfrontp += sizeof (doopt) - 2;
9816002Sroot }
9826002Sroot 
9836002Sroot dooption(option)
9846002Sroot 	int option;
9856002Sroot {
9866002Sroot 	char *fmt;
9876002Sroot 
9886002Sroot 	switch (option) {
9896002Sroot 
9906002Sroot 	case TELOPT_TM:
9916002Sroot 		fmt = wont;
9926002Sroot 		break;
9936002Sroot 
9946002Sroot 	case TELOPT_ECHO:
9956002Sroot 		mode(ECHO|CRMOD, 0);
99627188Sminshall 		fmt = will;
99727188Sminshall 		break;
9986002Sroot 
9996002Sroot 	case TELOPT_BINARY:
10006002Sroot 		mode(RAW, 0);
100127188Sminshall 		fmt = will;
100227188Sminshall 		break;
10036002Sroot 
10046002Sroot 	case TELOPT_SGA:
10056002Sroot 		fmt = will;
10066002Sroot 		break;
10076002Sroot 
10086002Sroot 	default:
10096002Sroot 		fmt = wont;
10106002Sroot 		break;
10116002Sroot 	}
101227188Sminshall 	if (fmt == will) {
101327983Sminshall 	    myopts[option] = OPT_YES;
101427188Sminshall 	} else {
101527983Sminshall 	    myopts[option] = OPT_NO;
101627188Sminshall 	}
101732452Sbostic 	(void) sprintf(nfrontp, fmt, option);
10188379Ssam 	nfrontp += sizeof (doopt) - 2;
10196002Sroot }
10206002Sroot 
102127649Sminshall 
102227649Sminshall dontoption(option)
102327649Sminshall int option;
102427649Sminshall {
102527649Sminshall     char *fmt;
102627649Sminshall 
102727649Sminshall     switch (option) {
102827649Sminshall     case TELOPT_ECHO:		/* we should stop echoing */
102932401Sminshall 	mode(0, ECHO);
103027649Sminshall 	fmt = wont;
103127649Sminshall 	break;
103227983Sminshall 
103327649Sminshall     default:
103427649Sminshall 	fmt = wont;
103527649Sminshall 	break;
103627649Sminshall     }
103727983Sminshall 
103827649Sminshall     if (fmt = wont) {
103927983Sminshall 	myopts[option] = OPT_NO;
104027649Sminshall     } else {
104127983Sminshall 	myopts[option] = OPT_YES;
104227649Sminshall     }
104332452Sbostic     (void) sprintf(nfrontp, fmt, option);
104427649Sminshall     nfrontp += sizeof (wont) - 2;
104527649Sminshall }
104627649Sminshall 
1047*37210Sminshall char *ttyspeeds[] = {
1048*37210Sminshall 	"0", "50", "75", "110", "134", "150", "200", "300",
1049*37210Sminshall 	"600", "1200", "1800", "2400", "4800", "9600", "19200", "38400" };
1050*37210Sminshall #define NUMSPEEDS sizeof ttyspeeds/sizeof ttyspeeds[0]
1051*37210Sminshall 
1052*37210Sminshall string2speed(s)
1053*37210Sminshall   char *s;
1054*37210Sminshall {
1055*37210Sminshall   int i;
1056*37210Sminshall 
1057*37210Sminshall   for (i = 0; i < NUMSPEEDS; i++)
1058*37210Sminshall     if (strcmp(s, ttyspeeds[i]) == 0)
1059*37210Sminshall       return(i);
1060*37210Sminshall 
1061*37210Sminshall   return(0);
1062*37210Sminshall }
1063*37210Sminshall 
106427649Sminshall /*
106527649Sminshall  * suboption()
106627649Sminshall  *
106727649Sminshall  *	Look at the sub-option buffer, and try to be helpful to the other
106827649Sminshall  * side.
106927649Sminshall  *
107027649Sminshall  *	Currently we recognize:
107127649Sminshall  *
107227983Sminshall  *	Terminal type is
1073*37210Sminshall  *	Terminal size
1074*37210Sminshall  *	Terminal speed is
107527649Sminshall  */
107627649Sminshall 
107727649Sminshall suboption()
107827649Sminshall {
107927983Sminshall     switch (SB_GET()) {
108027983Sminshall     case TELOPT_TTYPE: {		/* Yaaaay! */
108127983Sminshall 	static char terminalname[5+41] = "TERM=";
108227983Sminshall 
108327983Sminshall 	settimer(ttypesubopt);
108427983Sminshall 
108527983Sminshall 	if (SB_GET() != TELQUAL_IS) {
108627983Sminshall 	    return;		/* ??? XXX but, this is the most robust */
108727983Sminshall 	}
108827983Sminshall 
108927983Sminshall 	terminaltype = terminalname+strlen(terminalname);
109027983Sminshall 
109127983Sminshall 	while ((terminaltype < (terminalname + sizeof terminalname-1)) &&
109227983Sminshall 								    !SB_EOF()) {
109327983Sminshall 	    register int c;
109427983Sminshall 
109527983Sminshall 	    c = SB_GET();
109627983Sminshall 	    if (isupper(c)) {
109727983Sminshall 		c = tolower(c);
109827983Sminshall 	    }
109927983Sminshall 	    *terminaltype++ = c;    /* accumulate name */
110027983Sminshall 	}
110127983Sminshall 	*terminaltype = 0;
110227983Sminshall 	terminaltype = terminalname;
110327983Sminshall 	break;
110427983Sminshall     }
1105*37210Sminshall     case TELOPT_NAWS: {
1106*37210Sminshall 	struct winsize win;
1107*37210Sminshall 	char c;
110827983Sminshall 
1109*37210Sminshall #define SB_GETCHAR(c) \
1110*37210Sminshall 	{ if ((c = SB_GET()) == IAC && SB_GET() != IAC) return; }
1111*37210Sminshall 
1112*37210Sminshall 	ioctl(pty, TIOCGWINSZ, &win);
1113*37210Sminshall 	settimer(ttypesubopt);
1114*37210Sminshall 
1115*37210Sminshall 	syslog(LOG_INFO, "%x %x %x %x",
1116*37210Sminshall 	subpointer[0],subpointer[1],subpointer[2],subpointer[3]);
1117*37210Sminshall 	SB_GETCHAR(c);
1118*37210Sminshall 	win.ws_col = c << 8;
1119*37210Sminshall 	SB_GETCHAR(c);
1120*37210Sminshall 	win.ws_col |= c;
1121*37210Sminshall 	SB_GETCHAR(c);
1122*37210Sminshall 	win.ws_row = c << 8;
1123*37210Sminshall 	SB_GETCHAR(c);
1124*37210Sminshall 	win.ws_row |= c;
1125*37210Sminshall 	syslog(LOG_INFO, "col %d row %d", win.ws_col, win.ws_row);
1126*37210Sminshall 	ioctl(pty, TIOCSWINSZ, &win);
1127*37210Sminshall 	break;
1128*37210Sminshall     }
1129*37210Sminshall     case TELOPT_TSPEED: {
1130*37210Sminshall 	char speeds[41],*cp=speeds;
1131*37210Sminshall 	struct sgttyb b;
1132*37210Sminshall 	int ispeed,ospeed;
1133*37210Sminshall 	char *ispeeds,*ospeeds;
1134*37210Sminshall 
1135*37210Sminshall 	if (SB_GET() != TELQUAL_IS) {
1136*37210Sminshall 	    return;             /* ??? XXX but, this is the most robust */
1137*37210Sminshall 	}
1138*37210Sminshall 
1139*37210Sminshall 	ispeeds = NULL;
1140*37210Sminshall 	ospeeds = speeds;
1141*37210Sminshall 	ispeed = 0;
1142*37210Sminshall 	ospeed = 0;
1143*37210Sminshall 	while ((cp < (speeds + sizeof speeds-1)) && !SB_EOF()) {
1144*37210Sminshall 	    register int c;
1145*37210Sminshall 
1146*37210Sminshall 	    c = SB_GET();
1147*37210Sminshall 	    if (c == ',') {
1148*37210Sminshall 		c = 0;
1149*37210Sminshall 		ispeeds = cp+1;
1150*37210Sminshall 	    }
1151*37210Sminshall 	    *cp++ = c;    /* accumulate name */
1152*37210Sminshall 	}
1153*37210Sminshall 	*cp = 0;
1154*37210Sminshall 
1155*37210Sminshall 	if (ispeeds)
1156*37210Sminshall 	    ispeed = string2speed(ispeeds);
1157*37210Sminshall 	if (ospeeds)
1158*37210Sminshall 	    ospeed = string2speed(ospeeds);
1159*37210Sminshall 
1160*37210Sminshall 	if (ispeed && ospeed) {
1161*37210Sminshall 	    ioctl(pty, TIOCGETP, &b);
1162*37210Sminshall 	    b.sg_ospeed = ospeed;
1163*37210Sminshall 	    b.sg_ispeed = ispeed;
1164*37210Sminshall 	    ioctl(pty, TIOCSETP, &b);
1165*37210Sminshall 	}
1166*37210Sminshall 
1167*37210Sminshall 	break;
1168*37210Sminshall     }
116927649Sminshall     default:
117027649Sminshall 	;
117127649Sminshall     }
117227649Sminshall }
117327649Sminshall 
11746002Sroot mode(on, off)
11756002Sroot 	int on, off;
11766002Sroot {
11776002Sroot 	struct sgttyb b;
11786002Sroot 
11796002Sroot 	ptyflush();
11806002Sroot 	ioctl(pty, TIOCGETP, &b);
11816002Sroot 	b.sg_flags |= on;
11826002Sroot 	b.sg_flags &= ~off;
11836002Sroot 	ioctl(pty, TIOCSETP, &b);
11846002Sroot }
11856002Sroot 
11866002Sroot /*
11876002Sroot  * Send interrupt to process on other side of pty.
11886002Sroot  * If it is in raw mode, just write NULL;
11896002Sroot  * otherwise, write intr char.
11906002Sroot  */
11916002Sroot interrupt()
11926002Sroot {
11936002Sroot 	struct sgttyb b;
11946002Sroot 	struct tchars tchars;
11956002Sroot 
11966002Sroot 	ptyflush();	/* half-hearted */
11976002Sroot 	ioctl(pty, TIOCGETP, &b);
11986002Sroot 	if (b.sg_flags & RAW) {
11996002Sroot 		*pfrontp++ = '\0';
12006002Sroot 		return;
12016002Sroot 	}
12026002Sroot 	*pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ?
12036002Sroot 		'\177' : tchars.t_intrc;
12046002Sroot }
12056002Sroot 
120627229Sminshall /*
120727229Sminshall  * Send quit to process on other side of pty.
120827229Sminshall  * If it is in raw mode, just write NULL;
120927229Sminshall  * otherwise, write quit char.
121027229Sminshall  */
121127229Sminshall sendbrk()
121227229Sminshall {
121327229Sminshall 	struct sgttyb b;
121427229Sminshall 	struct tchars tchars;
121527229Sminshall 
121627229Sminshall 	ptyflush();	/* half-hearted */
121727229Sminshall 	ioctl(pty, TIOCGETP, &b);
121827229Sminshall 	if (b.sg_flags & RAW) {
121927229Sminshall 		*pfrontp++ = '\0';
122027229Sminshall 		return;
122127229Sminshall 	}
122227229Sminshall 	*pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ?
122327229Sminshall 		'\034' : tchars.t_quitc;
122427229Sminshall }
122527229Sminshall 
12266002Sroot ptyflush()
12276002Sroot {
12286002Sroot 	int n;
12296002Sroot 
12306002Sroot 	if ((n = pfrontp - pbackp) > 0)
12316002Sroot 		n = write(pty, pbackp, n);
12328346Ssam 	if (n < 0)
12338346Ssam 		return;
12346002Sroot 	pbackp += n;
12356002Sroot 	if (pbackp == pfrontp)
12366002Sroot 		pbackp = pfrontp = ptyobuf;
12376002Sroot }
123827260Sminshall 
123927260Sminshall /*
124027260Sminshall  * nextitem()
124127260Sminshall  *
124227260Sminshall  *	Return the address of the next "item" in the TELNET data
124327260Sminshall  * stream.  This will be the address of the next character if
124427260Sminshall  * the current address is a user data character, or it will
124527260Sminshall  * be the address of the character following the TELNET command
124627260Sminshall  * if the current address is a TELNET IAC ("I Am a Command")
124727260Sminshall  * character.
124827260Sminshall  */
12496002Sroot 
125027260Sminshall char *
125127260Sminshall nextitem(current)
125227260Sminshall char	*current;
12536002Sroot {
125427260Sminshall     if ((*current&0xff) != IAC) {
125527260Sminshall 	return current+1;
125627260Sminshall     }
125727260Sminshall     switch (*(current+1)&0xff) {
125827260Sminshall     case DO:
125927260Sminshall     case DONT:
126027260Sminshall     case WILL:
126127260Sminshall     case WONT:
126227260Sminshall 	return current+3;
126327260Sminshall     case SB:		/* loop forever looking for the SE */
126427260Sminshall 	{
126527260Sminshall 	    register char *look = current+2;
12666002Sroot 
126727260Sminshall 	    for (;;) {
126827260Sminshall 		if ((*look++&0xff) == IAC) {
126927260Sminshall 		    if ((*look++&0xff) == SE) {
127027260Sminshall 			return look;
127127260Sminshall 		    }
127227260Sminshall 		}
127327260Sminshall 	    }
12748346Ssam 	}
127527260Sminshall     default:
127627260Sminshall 	return current+2;
127727260Sminshall     }
12786002Sroot }
12796002Sroot 
128027185Sminshall 
128127185Sminshall /*
128227260Sminshall  * netclear()
128327260Sminshall  *
128427260Sminshall  *	We are about to do a TELNET SYNCH operation.  Clear
128527260Sminshall  * the path to the network.
128627260Sminshall  *
128727260Sminshall  *	Things are a bit tricky since we may have sent the first
128827260Sminshall  * byte or so of a previous TELNET command into the network.
128927260Sminshall  * So, we have to scan the network buffer from the beginning
129027260Sminshall  * until we are up to where we want to be.
129127260Sminshall  *
129227260Sminshall  *	A side effect of what we do, just to keep things
129327260Sminshall  * simple, is to clear the urgent data pointer.  The principal
129427260Sminshall  * caller should be setting the urgent data pointer AFTER calling
129527260Sminshall  * us in any case.
129627260Sminshall  */
129727260Sminshall 
129827260Sminshall netclear()
129927260Sminshall {
130027260Sminshall     register char *thisitem, *next;
130127260Sminshall     char *good;
130227260Sminshall #define	wewant(p)	((nfrontp > p) && ((*p&0xff) == IAC) && \
130327260Sminshall 				((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
130427260Sminshall 
130527260Sminshall     thisitem = netobuf;
130627260Sminshall 
130727260Sminshall     while ((next = nextitem(thisitem)) <= nbackp) {
130827260Sminshall 	thisitem = next;
130927260Sminshall     }
131027260Sminshall 
131127260Sminshall     /* Now, thisitem is first before/at boundary. */
131227260Sminshall 
131327260Sminshall     good = netobuf;	/* where the good bytes go */
131427260Sminshall 
131527260Sminshall     while (nfrontp > thisitem) {
131627260Sminshall 	if (wewant(thisitem)) {
131727260Sminshall 	    int length;
131827260Sminshall 
131927260Sminshall 	    next = thisitem;
132027260Sminshall 	    do {
132127260Sminshall 		next = nextitem(next);
132227260Sminshall 	    } while (wewant(next) && (nfrontp > next));
132327260Sminshall 	    length = next-thisitem;
132427260Sminshall 	    bcopy(thisitem, good, length);
132527260Sminshall 	    good += length;
132627260Sminshall 	    thisitem = next;
132727260Sminshall 	} else {
132827260Sminshall 	    thisitem = nextitem(thisitem);
132927260Sminshall 	}
133027260Sminshall     }
133127260Sminshall 
133227260Sminshall     nbackp = netobuf;
133327260Sminshall     nfrontp = good;		/* next byte to be sent */
133427260Sminshall     neturg = 0;
133527260Sminshall }
133627260Sminshall 
133727260Sminshall /*
133827185Sminshall  *  netflush
133927185Sminshall  *		Send as much data as possible to the network,
134027185Sminshall  *	handling requests for urgent data.
134127185Sminshall  */
134227185Sminshall 
134327185Sminshall 
134427185Sminshall netflush()
134527185Sminshall {
134627185Sminshall     int n;
134727185Sminshall 
134827185Sminshall     if ((n = nfrontp - nbackp) > 0) {
134927649Sminshall 	/*
135027649Sminshall 	 * if no urgent data, or if the other side appears to be an
135127649Sminshall 	 * old 4.2 client (and thus unable to survive TCP urgent data),
135227649Sminshall 	 * write the entire buffer in non-OOB mode.
135327649Sminshall 	 */
135427649Sminshall 	if ((neturg == 0) || (not42 == 0)) {
135527185Sminshall 	    n = write(net, nbackp, n);	/* normal write */
135627185Sminshall 	} else {
135727185Sminshall 	    n = neturg - nbackp;
135827185Sminshall 	    /*
135927185Sminshall 	     * In 4.2 (and 4.3) systems, there is some question about
136027185Sminshall 	     * what byte in a sendOOB operation is the "OOB" data.
136127185Sminshall 	     * To make ourselves compatible, we only send ONE byte
136227185Sminshall 	     * out of band, the one WE THINK should be OOB (though
136327185Sminshall 	     * we really have more the TCP philosophy of urgent data
136427185Sminshall 	     * rather than the Unix philosophy of OOB data).
136527185Sminshall 	     */
136627185Sminshall 	    if (n > 1) {
136727185Sminshall 		n = send(net, nbackp, n-1, 0);	/* send URGENT all by itself */
136827185Sminshall 	    } else {
136927185Sminshall 		n = send(net, nbackp, n, MSG_OOB);	/* URGENT data */
137027185Sminshall 	    }
137127185Sminshall 	}
137227185Sminshall     }
137327185Sminshall     if (n < 0) {
137427185Sminshall 	if (errno == EWOULDBLOCK)
137527185Sminshall 	    return;
137627185Sminshall 	/* should blow this guy away... */
137727185Sminshall 	return;
137827185Sminshall     }
137927185Sminshall     nbackp += n;
138027185Sminshall     if (nbackp >= neturg) {
138127185Sminshall 	neturg = 0;
138227185Sminshall     }
138327185Sminshall     if (nbackp == nfrontp) {
138427185Sminshall 	nbackp = nfrontp = netobuf;
138527185Sminshall     }
138627185Sminshall }
138727185Sminshall 
13886002Sroot cleanup()
13896002Sroot {
139035443Sbostic 	char *p;
13916002Sroot 
139235443Sbostic 	p = line + sizeof("/dev/") - 1;
139335443Sbostic 	if (logout(p))
139435443Sbostic 		logwtmp(p, "", "");
139535443Sbostic 	(void)chmod(line, 0666);
139635443Sbostic 	(void)chown(line, 0, 0);
139735443Sbostic 	*p = 'p';
139835443Sbostic 	(void)chmod(line, 0666);
139935443Sbostic 	(void)chown(line, 0, 0);
140010191Ssam 	shutdown(net, 2);
14016002Sroot 	exit(1);
14026002Sroot }
14036002Sroot 
140433271Sminshall char	editedhost[32];
140533271Sminshall 
140633271Sminshall edithost(pat, host)
140733271Sminshall 	register char *pat;
140833271Sminshall 	register char *host;
140933271Sminshall {
141033271Sminshall 	register char *res = editedhost;
141133271Sminshall 
141233271Sminshall 	if (!pat)
141333271Sminshall 		pat = "";
141433271Sminshall 	while (*pat) {
141533271Sminshall 		switch (*pat) {
141633271Sminshall 
141733271Sminshall 		case '#':
141833271Sminshall 			if (*host)
141933271Sminshall 				host++;
142033271Sminshall 			break;
142133271Sminshall 
142233271Sminshall 		case '@':
142333271Sminshall 			if (*host)
142433271Sminshall 				*res++ = *host++;
142533271Sminshall 			break;
142633271Sminshall 
142733271Sminshall 		default:
142833271Sminshall 			*res++ = *pat;
142933271Sminshall 			break;
143033271Sminshall 
143133271Sminshall 		}
143233271Sminshall 		if (res == &editedhost[sizeof editedhost - 1]) {
143333271Sminshall 			*res = '\0';
143433271Sminshall 			return;
143533271Sminshall 		}
143633271Sminshall 		pat++;
143733271Sminshall 	}
143833271Sminshall 	if (*host)
143933271Sminshall 		strncpy(res, host, sizeof editedhost - (res - editedhost) - 1);
144033271Sminshall 	else
144133271Sminshall 		*res = '\0';
144233271Sminshall 	editedhost[sizeof editedhost - 1] = '\0';
144333271Sminshall }
144433271Sminshall 
144533271Sminshall static char *putlocation;
144633271Sminshall 
144733271Sminshall puts(s)
144833271Sminshall register char *s;
144933271Sminshall {
145033271Sminshall 
145133271Sminshall 	while (*s)
145233271Sminshall 		putchr(*s++);
145333271Sminshall }
145433271Sminshall 
145533271Sminshall putchr(cc)
145633271Sminshall {
145733271Sminshall 	*putlocation++ = cc;
145833271Sminshall }
145933271Sminshall 
146034424Sbostic putf(cp, where)
146133271Sminshall register char *cp;
146233271Sminshall char *where;
146333271Sminshall {
146433271Sminshall 	char *slash;
146533271Sminshall 	char datebuffer[60];
146633271Sminshall 	extern char *rindex();
146733271Sminshall 
146833271Sminshall 	putlocation = where;
146933271Sminshall 
147033271Sminshall 	while (*cp) {
147133271Sminshall 		if (*cp != '%') {
147233271Sminshall 			putchr(*cp++);
147333271Sminshall 			continue;
147433271Sminshall 		}
147533271Sminshall 		switch (*++cp) {
147633271Sminshall 
147733271Sminshall 		case 't':
147833271Sminshall 			slash = rindex(line, '/');
147933271Sminshall 			if (slash == (char *) 0)
148033271Sminshall 				puts(line);
148133271Sminshall 			else
148233271Sminshall 				puts(&slash[1]);
148333271Sminshall 			break;
148433271Sminshall 
148533271Sminshall 		case 'h':
148633271Sminshall 			puts(editedhost);
148733271Sminshall 			break;
148833271Sminshall 
148933271Sminshall 		case 'd':
149033271Sminshall 			get_date(datebuffer);
149133271Sminshall 			puts(datebuffer);
149233271Sminshall 			break;
149333271Sminshall 
149433271Sminshall 		case '%':
149533271Sminshall 			putchr('%');
149633271Sminshall 			break;
149733271Sminshall 		}
149833271Sminshall 		cp++;
149933271Sminshall 	}
150033271Sminshall }
1501