xref: /csrg-svn/libexec/telnetd/state.c (revision 38905)
1*38905Sborman /*
2*38905Sborman  * Copyright (c) 1989 Regents of the University of California.
3*38905Sborman  * All rights reserved.
4*38905Sborman  *
5*38905Sborman  * Redistribution and use in source and binary forms are permitted
6*38905Sborman  * provided that the above copyright notice and this paragraph are
7*38905Sborman  * duplicated in all such forms and that any documentation,
8*38905Sborman  * advertising materials, and other materials related to such
9*38905Sborman  * distribution and use acknowledge that the software was developed
10*38905Sborman  * by the University of California, Berkeley.  The name of the
11*38905Sborman  * University may not be used to endorse or promote products derived
12*38905Sborman  * from this software without specific prior written permission.
13*38905Sborman  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14*38905Sborman  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15*38905Sborman  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16*38905Sborman  */
17*38905Sborman 
18*38905Sborman #ifndef lint
19*38905Sborman static char sccsid[] = "@(#)state.c	5.1 (Berkeley) 09/01/89";
20*38905Sborman #endif /* not lint */
21*38905Sborman 
22*38905Sborman #include "telnetd.h"
23*38905Sborman 
24*38905Sborman char	doopt[] = { IAC, DO, '%', 'c', 0 };
25*38905Sborman char	dont[] = { IAC, DONT, '%', 'c', 0 };
26*38905Sborman char	will[] = { IAC, WILL, '%', 'c', 0 };
27*38905Sborman char	wont[] = { IAC, WONT, '%', 'c', 0 };
28*38905Sborman int	not42 = 1;
29*38905Sborman 
30*38905Sborman /*
31*38905Sborman  * Buffer for sub-options, and macros
32*38905Sborman  * for suboptions buffer manipulations
33*38905Sborman  */
34*38905Sborman char	subbuffer[100], *subpointer= subbuffer, *subend= subbuffer;
35*38905Sborman 
36*38905Sborman #define	SB_CLEAR()	subpointer = subbuffer;
37*38905Sborman #define	SB_TERM()	{ subend = subpointer; SB_CLEAR(); }
38*38905Sborman #define	SB_ACCUM(c)	if (subpointer < (subbuffer+sizeof subbuffer)) { \
39*38905Sborman 				*subpointer++ = (c); \
40*38905Sborman 			}
41*38905Sborman #define	SB_GET()	((*subpointer++)&0xff)
42*38905Sborman #define	SB_EOF()	(subpointer >= subend)
43*38905Sborman 
44*38905Sborman 
45*38905Sborman 
46*38905Sborman /*
47*38905Sborman  * State for recv fsm
48*38905Sborman  */
49*38905Sborman #define	TS_DATA		0	/* base state */
50*38905Sborman #define	TS_IAC		1	/* look for double IAC's */
51*38905Sborman #define	TS_CR		2	/* CR-LF ->'s CR */
52*38905Sborman #define	TS_SB		3	/* throw away begin's... */
53*38905Sborman #define	TS_SE		4	/* ...end's (suboption negotiation) */
54*38905Sborman #define	TS_WILL		5	/* will option negotiation */
55*38905Sborman #define	TS_WONT		6	/* wont " */
56*38905Sborman #define	TS_DO		7	/* do " */
57*38905Sborman #define	TS_DONT		8	/* dont " */
58*38905Sborman 
59*38905Sborman telrcv()
60*38905Sborman {
61*38905Sborman 	register int c;
62*38905Sborman 	static int state = TS_DATA;
63*38905Sborman #ifdef	CRAY2
64*38905Sborman 	char *opfrontp = pfrontp;
65*38905Sborman #endif
66*38905Sborman 
67*38905Sborman 	while (ncc > 0) {
68*38905Sborman 		if ((&ptyobuf[BUFSIZ] - pfrontp) < 2)
69*38905Sborman 			break;
70*38905Sborman 		c = *netip++ & 0377, ncc--;
71*38905Sborman 		switch (state) {
72*38905Sborman 
73*38905Sborman 		case TS_CR:
74*38905Sborman 			state = TS_DATA;
75*38905Sborman 			/* Strip off \n or \0 after a \r */
76*38905Sborman 			if ((c == 0) || (c == '\n')) {
77*38905Sborman 				break;
78*38905Sborman 			}
79*38905Sborman 			/* FALL THROUGH */
80*38905Sborman 
81*38905Sborman 		case TS_DATA:
82*38905Sborman 			if (c == IAC) {
83*38905Sborman 				state = TS_IAC;
84*38905Sborman 				break;
85*38905Sborman 			}
86*38905Sborman 			/*
87*38905Sborman 			 * We now map \r\n ==> \r for pragmatic reasons.
88*38905Sborman 			 * Many client implementations send \r\n when
89*38905Sborman 			 * the user hits the CarriageReturn key.
90*38905Sborman 			 *
91*38905Sborman 			 * We USED to map \r\n ==> \n, since \r\n says
92*38905Sborman 			 * that we want to be in column 1 of the next
93*38905Sborman 			 * printable line, and \n is the standard
94*38905Sborman 			 * unix way of saying that (\r is only good
95*38905Sborman 			 * if CRMOD is set, which it normally is).
96*38905Sborman 			 */
97*38905Sborman 			if ((c == '\r') && (hisopts[TELOPT_BINARY] == OPT_NO)) {
98*38905Sborman 				/*
99*38905Sborman 				 * If we are operating in linemode,
100*38905Sborman 				 * convert to local end-of-line.
101*38905Sborman 				 */
102*38905Sborman 				if ((linemode) && (ncc > 0)&&('\n' == *netip)) {
103*38905Sborman 					netip++; ncc--;
104*38905Sborman 					c = '\n';
105*38905Sborman 				} else {
106*38905Sborman 					state = TS_CR;
107*38905Sborman 				}
108*38905Sborman 			}
109*38905Sborman 			*pfrontp++ = c;
110*38905Sborman 			break;
111*38905Sborman 
112*38905Sborman 		case TS_IAC:
113*38905Sborman gotiac:			switch (c) {
114*38905Sborman 
115*38905Sborman 			/*
116*38905Sborman 			 * Send the process on the pty side an
117*38905Sborman 			 * interrupt.  Do this with a NULL or
118*38905Sborman 			 * interrupt char; depending on the tty mode.
119*38905Sborman 			 */
120*38905Sborman 			case IP:
121*38905Sborman 				interrupt();
122*38905Sborman 				break;
123*38905Sborman 
124*38905Sborman 			case BREAK:
125*38905Sborman 				sendbrk();
126*38905Sborman 				break;
127*38905Sborman 
128*38905Sborman 			/*
129*38905Sborman 			 * Are You There?
130*38905Sborman 			 */
131*38905Sborman 			case AYT:
132*38905Sborman 				(void) strcpy(nfrontp, "\r\n[Yes]\r\n");
133*38905Sborman 				nfrontp += 9;
134*38905Sborman 				break;
135*38905Sborman 
136*38905Sborman 			/*
137*38905Sborman 			 * Abort Output
138*38905Sborman 			 */
139*38905Sborman 			case AO:
140*38905Sborman 			    {
141*38905Sborman 				ptyflush();	/* half-hearted */
142*38905Sborman 				init_termbuf();
143*38905Sborman 
144*38905Sborman 				if (slctab[SLC_AO].sptr &&
145*38905Sborman 				    *slctab[SLC_AO].sptr != '\377') {
146*38905Sborman 					*pfrontp++ = *slctab[SLC_AO].sptr;
147*38905Sborman 				}
148*38905Sborman 
149*38905Sborman 				netclear();	/* clear buffer back */
150*38905Sborman 				*nfrontp++ = IAC;
151*38905Sborman 				*nfrontp++ = DM;
152*38905Sborman 				neturg = nfrontp-1; /* off by one XXX */
153*38905Sborman 				break;
154*38905Sborman 			    }
155*38905Sborman 
156*38905Sborman 			/*
157*38905Sborman 			 * Erase Character and
158*38905Sborman 			 * Erase Line
159*38905Sborman 			 */
160*38905Sborman 			case EC:
161*38905Sborman 			case EL:
162*38905Sborman 			    {
163*38905Sborman 				unsigned char ch;
164*38905Sborman 
165*38905Sborman 				ptyflush();	/* half-hearted */
166*38905Sborman 				init_termbuf();
167*38905Sborman 				ch = (c == EC) ? *slctab[SLC_EC].sptr :
168*38905Sborman 						 *slctab[SLC_EL].sptr;
169*38905Sborman 				if (ch != '\377')
170*38905Sborman 					*pfrontp++ = ch;
171*38905Sborman 				break;
172*38905Sborman 			    }
173*38905Sborman 
174*38905Sborman 			/*
175*38905Sborman 			 * Check for urgent data...
176*38905Sborman 			 */
177*38905Sborman 			case DM:
178*38905Sborman 				SYNCHing = stilloob(net);
179*38905Sborman 				settimer(gotDM);
180*38905Sborman 				break;
181*38905Sborman 
182*38905Sborman 
183*38905Sborman 			/*
184*38905Sborman 			 * Begin option subnegotiation...
185*38905Sborman 			 */
186*38905Sborman 			case SB:
187*38905Sborman 				state = TS_SB;
188*38905Sborman 				SB_CLEAR();
189*38905Sborman 				continue;
190*38905Sborman 
191*38905Sborman 			case WILL:
192*38905Sborman 				state = TS_WILL;
193*38905Sborman 				continue;
194*38905Sborman 
195*38905Sborman 			case WONT:
196*38905Sborman 				state = TS_WONT;
197*38905Sborman 				continue;
198*38905Sborman 
199*38905Sborman 			case DO:
200*38905Sborman 				state = TS_DO;
201*38905Sborman 				continue;
202*38905Sborman 
203*38905Sborman 			case DONT:
204*38905Sborman 				state = TS_DONT;
205*38905Sborman 				continue;
206*38905Sborman 			case EOR:
207*38905Sborman 				if (hisopts[TELOPT_EOR])
208*38905Sborman 					doeof();
209*38905Sborman 				break;
210*38905Sborman 
211*38905Sborman 			/*
212*38905Sborman 			 * Handle RFC 10xx Telnet linemode option additions
213*38905Sborman 			 * to command stream (EOF, SUSP, ABORT).
214*38905Sborman 			 */
215*38905Sborman 			case xEOF:
216*38905Sborman 				doeof();
217*38905Sborman 				break;
218*38905Sborman 
219*38905Sborman 			case SUSP:
220*38905Sborman 				sendsusp();
221*38905Sborman 				break;
222*38905Sborman 
223*38905Sborman 			case ABORT:
224*38905Sborman 				sendbrk();
225*38905Sborman 				break;
226*38905Sborman 
227*38905Sborman 			case IAC:
228*38905Sborman 				*pfrontp++ = c;
229*38905Sborman 				break;
230*38905Sborman 			}
231*38905Sborman 			state = TS_DATA;
232*38905Sborman 			break;
233*38905Sborman 
234*38905Sborman 		case TS_SB:
235*38905Sborman 			if (c == IAC) {
236*38905Sborman 				state = TS_SE;
237*38905Sborman 			} else {
238*38905Sborman 				SB_ACCUM(c);
239*38905Sborman 			}
240*38905Sborman 			break;
241*38905Sborman 
242*38905Sborman 		case TS_SE:
243*38905Sborman 			if (c != SE) {
244*38905Sborman 				if (c != IAC) {
245*38905Sborman 					/*
246*38905Sborman 					 * bad form of suboption negotiation.
247*38905Sborman 					 * handle it in such a way as to avoid
248*38905Sborman 					 * damage to local state.  Parse
249*38905Sborman 					 * suboption buffer found so far,
250*38905Sborman 					 * then treat remaining stream as
251*38905Sborman 					 * another command sequence.
252*38905Sborman 					 */
253*38905Sborman 					SB_TERM();
254*38905Sborman 					suboption();
255*38905Sborman 					state = TS_IAC;
256*38905Sborman 					goto gotiac;
257*38905Sborman 				}
258*38905Sborman 				SB_ACCUM(c);
259*38905Sborman 				state = TS_SB;
260*38905Sborman 			} else {
261*38905Sborman 				SB_TERM();
262*38905Sborman 				suboption();	/* handle sub-option */
263*38905Sborman 				state = TS_DATA;
264*38905Sborman 			}
265*38905Sborman 			break;
266*38905Sborman 
267*38905Sborman 		case TS_WILL:
268*38905Sborman 			willoption(c, 0);
269*38905Sborman 			state = TS_DATA;
270*38905Sborman 			continue;
271*38905Sborman 
272*38905Sborman 		case TS_WONT:
273*38905Sborman 			wontoption(c, 0);
274*38905Sborman 			state = TS_DATA;
275*38905Sborman 			continue;
276*38905Sborman 
277*38905Sborman 		case TS_DO:
278*38905Sborman 			dooption(c, 0);
279*38905Sborman 			state = TS_DATA;
280*38905Sborman 			continue;
281*38905Sborman 
282*38905Sborman 		case TS_DONT:
283*38905Sborman 			dontoption(c, 0);
284*38905Sborman 			state = TS_DATA;
285*38905Sborman 			continue;
286*38905Sborman 
287*38905Sborman 		default:
288*38905Sborman 			syslog(LOG_ERR, "telnetd: panic state=%d\n", state);
289*38905Sborman 			printf("telnetd: panic state=%d\n", state);
290*38905Sborman 			exit(1);
291*38905Sborman 		}
292*38905Sborman 	}
293*38905Sborman #if	CRAY2
294*38905Sborman 	if (!linemode) {
295*38905Sborman 		char	xptyobuf[BUFSIZ+NETSLOP];
296*38905Sborman 		char	xbuf2[BUFSIZ];
297*38905Sborman 		register char *cp;
298*38905Sborman 		int n = pfrontp - opfrontp, oc;
299*38905Sborman 		bcopy(opfrontp, xptyobuf, n);
300*38905Sborman 		pfrontp = opfrontp;
301*38905Sborman 		pfrontp += term_input(xptyobuf, pfrontp, n, BUFSIZ+NETSLOP,
302*38905Sborman 					xbuf2, &oc, BUFSIZ);
303*38905Sborman 		for (cp = xbuf2; oc > 0; --oc)
304*38905Sborman 			if ((*nfrontp++ = *cp++) == IAC)
305*38905Sborman 				*nfrontp++ = IAC;
306*38905Sborman 	}
307*38905Sborman #endif	/* CRAY2 */
308*38905Sborman }  /* end of telrcv */
309*38905Sborman 
310*38905Sborman /*
311*38905Sborman  * The will/wont/do/dont state machines are based on Dave Borman's
312*38905Sborman  * Telnet option processing state machine.  We keep track of the full
313*38905Sborman  * state of the option negotiation with the following state variables
314*38905Sborman  *	myopts, hisopts - The last fully negotiated state for each
315*38905Sborman  *			side of the connection.
316*38905Sborman  *	mywants, hiswants - The state we wish to be in after a completed
317*38905Sborman  *			negotiation.  (hiswants is slightly misleading,
318*38905Sborman  *			this is more precisely the state I want him to
319*38905Sborman  *			be in.
320*38905Sborman  *	resp - We count the number of requests we have sent out.
321*38905Sborman  *
322*38905Sborman  * These correspond to the following states:
323*38905Sborman  *	my_state = the last negotiated state
324*38905Sborman  *	want_state = what I want the state to go to
325*38905Sborman  *	want_resp = how many requests I have sent
326*38905Sborman  * All state defaults are negative, and resp defaults to 0.
327*38905Sborman  *
328*38905Sborman  * When initiating a request to change state to new_state:
329*38905Sborman  *
330*38905Sborman  * if ((want_resp == 0 && new_state == my_state) || want_state == new_state) {
331*38905Sborman  *	do nothing;
332*38905Sborman  * } else {
333*38905Sborman  *	want_state = new_state;
334*38905Sborman  *	send new_state;
335*38905Sborman  *	want_resp++;
336*38905Sborman  * }
337*38905Sborman  *
338*38905Sborman  * When receiving new_state:
339*38905Sborman  *
340*38905Sborman  * if (want_resp) {
341*38905Sborman  *	want_resp--;
342*38905Sborman  *	if (want_resp && (new_state == my_state))
343*38905Sborman  *		want_resp--;
344*38905Sborman  * }
345*38905Sborman  * if ((want_resp == 0) && (new_state != want_state)) {
346*38905Sborman  *	if (ok_to_switch_to new_state)
347*38905Sborman  *		want_state = new_state;
348*38905Sborman  *	else
349*38905Sborman  *		want_resp++;
350*38905Sborman  *	send want_state;
351*38905Sborman  * }
352*38905Sborman  * my_state = new_state;
353*38905Sborman  *
354*38905Sborman  * Note that new_state is implied in these functions by the function itself.
355*38905Sborman  * will and do imply positive new_state, wont and dont imply negative.
356*38905Sborman  *
357*38905Sborman  * Finally, there is one catch.  If we send a negative response to a
358*38905Sborman  * positive request, my_state will be the positive while want_state will
359*38905Sborman  * remain negative.  my_state will revert to negative when the negative
360*38905Sborman  * acknowlegment arrives from the peer.  Thus, my_state generally tells
361*38905Sborman  * us not only the last negotiated state, but also tells us what the peer
362*38905Sborman  * wants to be doing as well.  It is important to understand this difference
363*38905Sborman  * as we may wish to be processing data streams based on our desired state
364*38905Sborman  * (want_state) or based on what the peer thinks the state is (my_state).
365*38905Sborman  *
366*38905Sborman  * This all works fine because if the peer sends a positive request, the data
367*38905Sborman  * that we receive prior to negative acknowlegment will probably be affected
368*38905Sborman  * by the positive state, and we can process it as such (if we can; if we
369*38905Sborman  * can't then it really doesn't matter).  If it is that important, then the
370*38905Sborman  * peer probably should be buffering until this option state negotiation
371*38905Sborman  * is complete.
372*38905Sborman  *
373*38905Sborman  * In processing options, request signifies whether this is a request
374*38905Sborman  * to send or a response.  request is true if this is a request to
375*38905Sborman  * send generated locally.
376*38905Sborman  */
377*38905Sborman willoption(option, request)
378*38905Sborman 	int option, request;
379*38905Sborman {
380*38905Sborman 	int changeok = 0;
381*38905Sborman 	char *fmt = (char *)0;
382*38905Sborman 
383*38905Sborman     /*
384*38905Sborman      * process input from peer.
385*38905Sborman      */
386*38905Sborman     if (request == 0) {
387*38905Sborman 		switch (option) {
388*38905Sborman 
389*38905Sborman 		case TELOPT_BINARY:
390*38905Sborman 			init_termbuf();
391*38905Sborman 			tty_binaryin(1);
392*38905Sborman 			set_termbuf();
393*38905Sborman 			changeok++;
394*38905Sborman 			break;
395*38905Sborman 
396*38905Sborman 		case TELOPT_ECHO:
397*38905Sborman 			not42 = 0;		/* looks like a 4.2 system */
398*38905Sborman 			/*
399*38905Sborman 			 * Now, in a 4.2 system, to break them out of ECHOing
400*38905Sborman 			 * (to the terminal) mode, we need to send a
401*38905Sborman 			 * "WILL ECHO".  Kludge upon kludge!
402*38905Sborman 			 */
403*38905Sborman 			if (myopts[TELOPT_ECHO] == OPT_YES) {
404*38905Sborman 				dooption(TELOPT_ECHO, 1);
405*38905Sborman 			}
406*38905Sborman 			/*
407*38905Sborman 			 * Fool the state machine into sending a don't.
408*38905Sborman 			 * This also allows the initial echo sending code to
409*38905Sborman 			 * break out of the loop that it is
410*38905Sborman 			 * in.  (Look in telnet())
411*38905Sborman 			 */
412*38905Sborman 			hiswants[TELOPT_ECHO] = OPT_NO;
413*38905Sborman 			break;
414*38905Sborman 
415*38905Sborman 		case TELOPT_TM:
416*38905Sborman #if	defined(LINEMODE) && defined(KLUDGELINEMODE)
417*38905Sborman 			/*
418*38905Sborman 		 	 * This telnetd implementation does not really support
419*38905Sborman 			 * timing marks, it just uses them to support the kludge
420*38905Sborman 			 * linemode stuff.  If we receive a will or wont TM in
421*38905Sborman 			 * response to our do TM request that may have been sent
422*38905Sborman 			 * to determine kludge linemode support, process it,
423*38905Sborman 			 * otherwise TM should get a negative response back.
424*38905Sborman 			 */
425*38905Sborman 			/*
426*38905Sborman 			 * Handle the linemode kludge stuff.
427*38905Sborman 			 * If we are not currently supporting any
428*38905Sborman 			 * linemode at all, then we assume that this
429*38905Sborman 			 * is the client telling us to use kludge
430*38905Sborman 			 * linemode in response to our query.  Set the
431*38905Sborman 			 * linemode type that is to be supported, note
432*38905Sborman 			 * that the client wishes to use linemode, and
433*38905Sborman 			 * eat the will TM as though it never arrived.
434*38905Sborman 			 */
435*38905Sborman 			if (lmodetype < KLUDGE_LINEMODE) {
436*38905Sborman 				lmodetype = KLUDGE_LINEMODE;
437*38905Sborman 				clientstat(TELOPT_LINEMODE, WILL, 0);
438*38905Sborman 				dontoption(TELOPT_SGA, 0);
439*38905Sborman 			}
440*38905Sborman #endif	/* defined(LINEMODE) && defined(KLUDGELINEMODE) */
441*38905Sborman 			/*
442*38905Sborman 			 * cheat the state machine so that it
443*38905Sborman 			 * looks like we never sent the TM at
444*38905Sborman 			 * all.  The bad part of this is that
445*38905Sborman 			 * if the client sends a will TM on his
446*38905Sborman 			 * own to turn on linemode, then he
447*38905Sborman 			 * won't get a response.
448*38905Sborman 			 */
449*38905Sborman 			hiswants[TELOPT_TM] = OPT_NO;
450*38905Sborman 			resp[TELOPT_TM]--;
451*38905Sborman 			return;
452*38905Sborman 
453*38905Sborman 		case TELOPT_LFLOW:
454*38905Sborman 			/*
455*38905Sborman 			 * If we are going to support flow control option,
456*38905Sborman 			 * then don't worry peer that we can't change the
457*38905Sborman 			 * flow control characters.
458*38905Sborman 			 */
459*38905Sborman 			slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS;
460*38905Sborman 			slctab[SLC_XON].defset.flag |= SLC_DEFAULT;
461*38905Sborman 			slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS;
462*38905Sborman 			slctab[SLC_XOFF].defset.flag |= SLC_DEFAULT;
463*38905Sborman 		case TELOPT_TTYPE:
464*38905Sborman 		case TELOPT_SGA:
465*38905Sborman 		case TELOPT_NAWS:
466*38905Sborman 		case TELOPT_TSPEED:
467*38905Sborman #ifdef	LINEMODE
468*38905Sborman 		case TELOPT_LINEMODE:
469*38905Sborman #endif	LINEMODE
470*38905Sborman 			changeok++;
471*38905Sborman 			break;
472*38905Sborman 
473*38905Sborman 		default:
474*38905Sborman 			break;
475*38905Sborman 		}
476*38905Sborman 
477*38905Sborman 	}
478*38905Sborman 
479*38905Sborman 	if (request) {
480*38905Sborman 		if (!((resp[option] == 0 && hisopts[option] == OPT_YES) ||
481*38905Sborman 		    hiswants[option] == OPT_YES)) {
482*38905Sborman 			hiswants[option] = OPT_YES;
483*38905Sborman 			fmt = doopt;
484*38905Sborman 			resp[option]++;
485*38905Sborman 		}
486*38905Sborman 	} else {
487*38905Sborman 		if (resp[option]) {
488*38905Sborman 			resp[option]--;
489*38905Sborman 			if (resp[option] && hisopts[option] == OPT_YES)
490*38905Sborman 				resp[option]--;
491*38905Sborman 		}
492*38905Sborman 		if ((resp[option] == 0) && (hiswants[option] != OPT_YES)) {
493*38905Sborman 			if (changeok)
494*38905Sborman 				hiswants[option] = OPT_YES;
495*38905Sborman 			else
496*38905Sborman 				resp[option]++;
497*38905Sborman 			fmt = (hiswants[option] ? doopt : dont);
498*38905Sborman 		}
499*38905Sborman 		hisopts[option] = OPT_YES;
500*38905Sborman 	}
501*38905Sborman 
502*38905Sborman 	if (fmt) {
503*38905Sborman 		(void) sprintf(nfrontp, fmt, option);
504*38905Sborman 		nfrontp += sizeof (dont) - 2;
505*38905Sborman 	}
506*38905Sborman 
507*38905Sborman 	/*
508*38905Sborman 	 * Handle other processing that should occur after we have
509*38905Sborman 	 * responded to client input.
510*38905Sborman 	 */
511*38905Sborman 	if (!request) {
512*38905Sborman 		switch (option) {
513*38905Sborman #ifdef	LINEMODE
514*38905Sborman 		case TELOPT_LINEMODE:
515*38905Sborman # ifdef	KLUDGELINEMODE
516*38905Sborman 			/*
517*38905Sborman 			 * Note client's desire to use linemode.
518*38905Sborman 			 */
519*38905Sborman 			lmodetype = REAL_LINEMODE;
520*38905Sborman # endif	/* KLUDGELINEMODE */
521*38905Sborman 			clientstat(TELOPT_LINEMODE, WILL, 0);
522*38905Sborman 			break;
523*38905Sborman #endif	LINEMODE
524*38905Sborman 
525*38905Sborman 		default:
526*38905Sborman 			break;
527*38905Sborman 		}
528*38905Sborman 	}
529*38905Sborman 
530*38905Sborman }  /* end of willoption */
531*38905Sborman 
532*38905Sborman wontoption(option, request)
533*38905Sborman 	int option, request;
534*38905Sborman {
535*38905Sborman 	char *fmt = (char *)0;
536*38905Sborman 
537*38905Sborman 	/*
538*38905Sborman 	 * Process client input.
539*38905Sborman 	 */
540*38905Sborman 	if (!request) {
541*38905Sborman 		switch (option) {
542*38905Sborman 		case TELOPT_ECHO:
543*38905Sborman 			not42 = 1;	/* doesn't seem to be a 4.2 system */
544*38905Sborman 			break;
545*38905Sborman 
546*38905Sborman 		case TELOPT_BINARY:
547*38905Sborman 			init_termbuf();
548*38905Sborman 			tty_binaryin(0);
549*38905Sborman 			set_termbuf();
550*38905Sborman 			break;
551*38905Sborman 
552*38905Sborman #ifdef	LINEMODE
553*38905Sborman 		case TELOPT_LINEMODE:
554*38905Sborman # ifdef	KLUDGELINEMODE
555*38905Sborman 			/*
556*38905Sborman 			 * If real linemode is supported, then client is
557*38905Sborman 			 * asking to turn linemode off.
558*38905Sborman 			 */
559*38905Sborman 			if (lmodetype == REAL_LINEMODE)
560*38905Sborman # endif	/* KLUDGELINEMODE */
561*38905Sborman 				clientstat(TELOPT_LINEMODE, WONT, 0);
562*38905Sborman 			break;
563*38905Sborman #endif	LINEMODE
564*38905Sborman 
565*38905Sborman 		case TELOPT_TM:
566*38905Sborman #if	defined(LINEMODE) && defined(KLUDGELINEMODE)
567*38905Sborman 			if (lmodetype < REAL_LINEMODE) {
568*38905Sborman 				lmodetype = NO_LINEMODE;
569*38905Sborman 				clientstat(TELOPT_LINEMODE, WONT, 0);
570*38905Sborman 				dooption(TELOPT_SGA, 0);
571*38905Sborman 			}
572*38905Sborman #endif	/* defined(LINEMODE) && defined(KLUDGELINEMODE) */
573*38905Sborman 			/*
574*38905Sborman 			 * If we get a WONT TM, and had sent a DO TM, don't
575*38905Sborman 			 * respond with a DONT TM, just leave it as is.
576*38905Sborman 			 * Short circut the state machine to achive this.
577*38905Sborman 			 * The bad part of this is that if the client sends
578*38905Sborman 			 * a WONT TM on his own to turn off linemode, then he
579*38905Sborman 			 * won't get a response.
580*38905Sborman 			 */
581*38905Sborman 			hiswants[TELOPT_TM] = OPT_NO;
582*38905Sborman 			resp[TELOPT_TM]--;
583*38905Sborman 			return;
584*38905Sborman 
585*38905Sborman 		case TELOPT_LFLOW:
586*38905Sborman 			/*
587*38905Sborman 			 * If we are not going to support flow control option,
588*38905Sborman 			 * then let peer know that we can't change the
589*38905Sborman 			 * flow control characters.
590*38905Sborman 			 */
591*38905Sborman 			slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS;
592*38905Sborman 			slctab[SLC_XON].defset.flag |= SLC_CANTCHANGE;
593*38905Sborman 			slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS;
594*38905Sborman 			slctab[SLC_XOFF].defset.flag |= SLC_CANTCHANGE;
595*38905Sborman 			break;
596*38905Sborman 
597*38905Sborman 		default:
598*38905Sborman 			break;
599*38905Sborman 		}
600*38905Sborman 	}
601*38905Sborman 
602*38905Sborman 
603*38905Sborman 	if (request) {
604*38905Sborman 		if (!((resp[option] == 0 && hisopts[option] == OPT_NO) ||
605*38905Sborman 			hiswants[option] == OPT_NO)) {
606*38905Sborman 			hiswants[option] = OPT_NO;
607*38905Sborman 			fmt = dont;
608*38905Sborman 			resp[option]++;
609*38905Sborman 		}
610*38905Sborman 	} else {
611*38905Sborman 		if (resp[option]) {
612*38905Sborman 			resp[option]--;
613*38905Sborman 			if (resp[option] && hisopts[option] == OPT_NO)
614*38905Sborman 				resp[option]--;
615*38905Sborman 		}
616*38905Sborman 		if ((resp[option] == 0) && (hiswants[option] != OPT_NO)) {
617*38905Sborman 			/* it is always ok to change to negative state */
618*38905Sborman 			hiswants[option] = OPT_NO;
619*38905Sborman 			fmt = dont;
620*38905Sborman 		}
621*38905Sborman 		hisopts[option] = OPT_NO;
622*38905Sborman 	}
623*38905Sborman 
624*38905Sborman 	if (fmt) {
625*38905Sborman 		(void) sprintf(nfrontp, fmt, option);
626*38905Sborman 		nfrontp += sizeof (doopt) - 2;
627*38905Sborman 	}
628*38905Sborman 
629*38905Sborman }  /* end of wontoption */
630*38905Sborman 
631*38905Sborman dooption(option, request)
632*38905Sborman 	int option, request;
633*38905Sborman {
634*38905Sborman 	int changeok = 0;
635*38905Sborman 	char *fmt = (char *)0;
636*38905Sborman 
637*38905Sborman 	/*
638*38905Sborman 	 * Process client input.
639*38905Sborman 	 */
640*38905Sborman 	if (!request) {
641*38905Sborman 		switch (option) {
642*38905Sborman 		case TELOPT_ECHO:
643*38905Sborman #ifdef	LINEMODE
644*38905Sborman 			if (hisopts[TELOPT_LINEMODE] == OPT_NO) {
645*38905Sborman #endif
646*38905Sborman 				init_termbuf();
647*38905Sborman 				tty_setecho(1);
648*38905Sborman 				set_termbuf();
649*38905Sborman #ifdef	LINEMODE
650*38905Sborman 			}
651*38905Sborman #endif
652*38905Sborman 			changeok++;
653*38905Sborman 			break;
654*38905Sborman 
655*38905Sborman 		case TELOPT_BINARY:
656*38905Sborman 			init_termbuf();
657*38905Sborman 			tty_binaryout(1);
658*38905Sborman 			set_termbuf();
659*38905Sborman 			changeok++;
660*38905Sborman 			break;
661*38905Sborman 
662*38905Sborman 		case TELOPT_SGA:
663*38905Sborman #if	defined(LINEMODE) && defined(KLUDGELINEMODE)
664*38905Sborman 			/*
665*38905Sborman 			 * If kludge linemode is in use, then we must process
666*38905Sborman 			 * an incoming do SGA for linemode purposes.
667*38905Sborman 			 */
668*38905Sborman 			if (lmodetype == KLUDGE_LINEMODE) {
669*38905Sborman 				/*
670*38905Sborman 				 * Receipt of "do SGA" in kludge linemode
671*38905Sborman 				 * is the peer asking us to turn off linemode.
672*38905Sborman 				 * Make note of the request.
673*38905Sborman 				 */
674*38905Sborman 				clientstat(TELOPT_LINEMODE, WONT, 0);
675*38905Sborman 				/*
676*38905Sborman 				 * If linemode did not get turned off then
677*38905Sborman 				 * don't tell peer that we did.  Breaking
678*38905Sborman 				 * here forces a wont SGA to be returned.
679*38905Sborman 				 */
680*38905Sborman 				if (linemode)
681*38905Sborman 					break;
682*38905Sborman 			}
683*38905Sborman #endif	/* defined(LINEMODE) && defined(KLUDGELINEMODE) */
684*38905Sborman 			changeok++;
685*38905Sborman 			break;
686*38905Sborman 
687*38905Sborman 		case TELOPT_STATUS:
688*38905Sborman 			changeok++;
689*38905Sborman 			break;
690*38905Sborman 
691*38905Sborman 		case TELOPT_TM:
692*38905Sborman 		case TELOPT_LINEMODE:
693*38905Sborman 		case TELOPT_TTYPE:
694*38905Sborman 		case TELOPT_NAWS:
695*38905Sborman 		case TELOPT_TSPEED:
696*38905Sborman 		case TELOPT_LFLOW:
697*38905Sborman 		default:
698*38905Sborman 			break;
699*38905Sborman 		}
700*38905Sborman 	}
701*38905Sborman 
702*38905Sborman 	if (request) {
703*38905Sborman 		if (!((resp[option] == 0 && myopts[option] == OPT_YES) ||
704*38905Sborman 		    mywants[option] == OPT_YES)) {
705*38905Sborman 			mywants[option] = OPT_YES;
706*38905Sborman 			fmt = will;
707*38905Sborman 			resp[option]++;
708*38905Sborman 		}
709*38905Sborman 	} else {
710*38905Sborman 		if (resp[option]) {
711*38905Sborman 			resp[option]--;
712*38905Sborman 			if (resp[option] && myopts[option] == OPT_YES)
713*38905Sborman 				resp[option]--;
714*38905Sborman 		}
715*38905Sborman 		if ((resp[option] == 0) && (mywants[option] != OPT_YES)) {
716*38905Sborman 			if (changeok)
717*38905Sborman 				mywants[option] = OPT_YES;
718*38905Sborman 			else
719*38905Sborman 				resp[option]++;
720*38905Sborman 			fmt = (mywants[option] ? will : wont);
721*38905Sborman 		}
722*38905Sborman 		myopts[option] = OPT_YES;
723*38905Sborman 	}
724*38905Sborman 
725*38905Sborman 	if (fmt) {
726*38905Sborman 		(void) sprintf(nfrontp, fmt, option);
727*38905Sborman 		nfrontp += sizeof (doopt) - 2;
728*38905Sborman 	}
729*38905Sborman 
730*38905Sborman }  /* end of dooption */
731*38905Sborman 
732*38905Sborman 
733*38905Sborman dontoption(option, request)
734*38905Sborman 	int option, request;
735*38905Sborman {
736*38905Sborman 	char *fmt = (char *)0;
737*38905Sborman 
738*38905Sborman 	/*
739*38905Sborman 	 * Process client input.
740*38905Sborman 	 */
741*38905Sborman 	if (!request) {
742*38905Sborman 		switch (option) {
743*38905Sborman 		case TELOPT_BINARY:
744*38905Sborman 			init_termbuf();
745*38905Sborman 			tty_binaryout(0);
746*38905Sborman 			set_termbuf();
747*38905Sborman 			break;
748*38905Sborman 
749*38905Sborman 		case TELOPT_ECHO:		/* we should stop echoing */
750*38905Sborman #ifdef	LINEMODE
751*38905Sborman 			if (hisopts[TELOPT_LINEMODE] == OPT_NO) {
752*38905Sborman #endif
753*38905Sborman 				init_termbuf();
754*38905Sborman 				tty_setecho(0);
755*38905Sborman 				set_termbuf();
756*38905Sborman #ifdef	LINEMODE
757*38905Sborman 			}
758*38905Sborman #endif
759*38905Sborman 			break;
760*38905Sborman 
761*38905Sborman 		case TELOPT_SGA:
762*38905Sborman #if	defined(LINEMODE) && defined(KLUDGELINEMODE)
763*38905Sborman 			/*
764*38905Sborman 			 * If kludge linemode is in use, then we must process an
765*38905Sborman 			 * incoming do SGA for linemode purposes.
766*38905Sborman 			 */
767*38905Sborman 			if (lmodetype == KLUDGE_LINEMODE) {
768*38905Sborman 				/*
769*38905Sborman 				 * The client is asking us to turn linemode
770*38905Sborman 				 * on.
771*38905Sborman 				 */
772*38905Sborman 				clientstat(TELOPT_LINEMODE, WILL, 0);
773*38905Sborman 				/*
774*38905Sborman 				 * If we did not turn line mode on, then what do
775*38905Sborman 				 * we say?  Will SGA?  This violates design of
776*38905Sborman 				 * telnet.  Gross.  Very Gross.
777*38905Sborman 				 */
778*38905Sborman 			}
779*38905Sborman #endif	/* defined(LINEMODE) && defined(KLUDGELINEMODE) */
780*38905Sborman 
781*38905Sborman 		default:
782*38905Sborman 			break;
783*38905Sborman 		}
784*38905Sborman 	}
785*38905Sborman 
786*38905Sborman 	if (request) {
787*38905Sborman 		if (!((resp[option] == 0 && myopts[option] == OPT_NO) ||
788*38905Sborman 		    mywants[option] == OPT_NO)) {
789*38905Sborman 			mywants[option] = OPT_NO;
790*38905Sborman 			fmt = wont;
791*38905Sborman 			resp[option]++;
792*38905Sborman 		}
793*38905Sborman 	} else {
794*38905Sborman 		if (resp[option]) {
795*38905Sborman 			resp[option]--;
796*38905Sborman 			if (resp[option] && myopts[option] == OPT_NO)
797*38905Sborman 				resp[option]--;
798*38905Sborman 		}
799*38905Sborman 		if ((resp[option] == 0) && (mywants[option] != OPT_NO)) {
800*38905Sborman 			mywants[option] = OPT_NO;
801*38905Sborman 			fmt = wont;
802*38905Sborman 		}
803*38905Sborman 		myopts[option] = OPT_NO;
804*38905Sborman 	}
805*38905Sborman 
806*38905Sborman 	if (fmt) {
807*38905Sborman 	    (void) sprintf(nfrontp, fmt, option);
808*38905Sborman 	    nfrontp += sizeof (wont) - 2;
809*38905Sborman 	}
810*38905Sborman 
811*38905Sborman }  /* end of dontoption */
812*38905Sborman 
813*38905Sborman /*
814*38905Sborman  * suboption()
815*38905Sborman  *
816*38905Sborman  *	Look at the sub-option buffer, and try to be helpful to the other
817*38905Sborman  * side.
818*38905Sborman  *
819*38905Sborman  *	Currently we recognize:
820*38905Sborman  *
821*38905Sborman  *	Terminal type is
822*38905Sborman  *	Linemode
823*38905Sborman  *	Window size
824*38905Sborman  *	Terminal speed
825*38905Sborman  */
826*38905Sborman suboption()
827*38905Sborman {
828*38905Sborman     register int subchar;
829*38905Sborman 
830*38905Sborman     subchar = SB_GET();
831*38905Sborman     switch (subchar) {
832*38905Sborman     case TELOPT_TSPEED: {
833*38905Sborman 	register int xspeed, rspeed;
834*38905Sborman 
835*38905Sborman 	if (hisopts[TELOPT_TSPEED] == OPT_NO)	/* Ignore if option disabled */
836*38905Sborman 		break;
837*38905Sborman 
838*38905Sborman 	settimer(tspeedsubopt);
839*38905Sborman 
840*38905Sborman 	if (SB_EOF() || SB_GET() != TELQUAL_IS)
841*38905Sborman 		return;
842*38905Sborman 
843*38905Sborman 	xspeed = atoi(subpointer);
844*38905Sborman 
845*38905Sborman 	while (SB_GET() != ',' && !SB_EOF());
846*38905Sborman 	if (SB_EOF())
847*38905Sborman 		return;
848*38905Sborman 
849*38905Sborman 	rspeed = atoi(subpointer);
850*38905Sborman 	clientstat(TELOPT_TSPEED, xspeed, rspeed);
851*38905Sborman 
852*38905Sborman 	break;
853*38905Sborman 
854*38905Sborman     }  /* end of case TELOPT_TSPEED */
855*38905Sborman 
856*38905Sborman     case TELOPT_TTYPE: {		/* Yaaaay! */
857*38905Sborman 	static char terminalname[5+41] = "TERM=";
858*38905Sborman 
859*38905Sborman 	if (hisopts[TELOPT_TSPEED] == OPT_NO)	/* Ignore if option disabled */
860*38905Sborman 		break;
861*38905Sborman 	settimer(ttypesubopt);
862*38905Sborman 
863*38905Sborman 	if (SB_GET() != TELQUAL_IS) {
864*38905Sborman 	    return;		/* ??? XXX but, this is the most robust */
865*38905Sborman 	}
866*38905Sborman 
867*38905Sborman 	terminaltype = terminalname+sizeof("TERM=")-1;
868*38905Sborman 
869*38905Sborman 	while ((terminaltype < (terminalname + sizeof terminalname-1)) &&
870*38905Sborman 								    !SB_EOF()) {
871*38905Sborman 	    register int c;
872*38905Sborman 
873*38905Sborman 	    c = SB_GET();
874*38905Sborman 	    if (isupper(c)) {
875*38905Sborman 		c = tolower(c);
876*38905Sborman 	    }
877*38905Sborman 	    *terminaltype++ = c;    /* accumulate name */
878*38905Sborman 	}
879*38905Sborman 	*terminaltype = 0;
880*38905Sborman 	terminaltype = terminalname;
881*38905Sborman 	break;
882*38905Sborman     }  /* end of case TELOPT_TTYPE */
883*38905Sborman 
884*38905Sborman     case TELOPT_NAWS: {
885*38905Sborman 	register int xwinsize, ywinsize;
886*38905Sborman 
887*38905Sborman 	if (hisopts[TELOPT_NAWS] == OPT_NO)	/* Ignore if option disabled */
888*38905Sborman 		break;
889*38905Sborman 
890*38905Sborman 	if (SB_EOF())
891*38905Sborman 		return;
892*38905Sborman 	xwinsize = SB_GET() << 8;
893*38905Sborman 	if (SB_EOF())
894*38905Sborman 		return;
895*38905Sborman 	xwinsize |= SB_GET();
896*38905Sborman 	if (SB_EOF())
897*38905Sborman 		return;
898*38905Sborman 	ywinsize = SB_GET() << 8;
899*38905Sborman 	if (SB_EOF())
900*38905Sborman 		return;
901*38905Sborman 	ywinsize |= SB_GET();
902*38905Sborman 	clientstat(TELOPT_NAWS, xwinsize, ywinsize);
903*38905Sborman 
904*38905Sborman 	break;
905*38905Sborman 
906*38905Sborman     }  /* end of case TELOPT_NAWS */
907*38905Sborman 
908*38905Sborman #ifdef	LINEMODE
909*38905Sborman     case TELOPT_LINEMODE: {
910*38905Sborman 	register int request;
911*38905Sborman 
912*38905Sborman 	if (hisopts[TELOPT_LINEMODE] == OPT_NO)	/* Ignore if option disabled */
913*38905Sborman 		break;
914*38905Sborman 	/*
915*38905Sborman 	 * Process linemode suboptions.
916*38905Sborman 	 */
917*38905Sborman 	if (SB_EOF()) break;  /* garbage was sent */
918*38905Sborman 	request = SB_GET();  /* get will/wont */
919*38905Sborman 	if (SB_EOF()) break;  /* another garbage check */
920*38905Sborman 
921*38905Sborman 	if (request == LM_SLC) {  /* SLC is not preceeded by WILL or WONT */
922*38905Sborman 		/*
923*38905Sborman 		 * Process suboption buffer of slc's
924*38905Sborman 		 */
925*38905Sborman 		start_slc(1);
926*38905Sborman 		do_opt_slc(subpointer, subend - subpointer);
927*38905Sborman 		end_slc(0);
928*38905Sborman 
929*38905Sborman 	} else if (request == LM_MODE) {
930*38905Sborman 		useeditmode = SB_GET();  /* get mode flag */
931*38905Sborman 		clientstat(LM_MODE, 0, 0);
932*38905Sborman 	}
933*38905Sborman 
934*38905Sborman 	switch (SB_GET()) {  /* what suboption? */
935*38905Sborman 	case LM_FORWARDMASK:
936*38905Sborman 		/*
937*38905Sborman 		 * According to spec, only server can send request for
938*38905Sborman 		 * forwardmask, and client can only return a positive response.
939*38905Sborman 		 * So don't worry about it.
940*38905Sborman 		 */
941*38905Sborman 
942*38905Sborman 	default:
943*38905Sborman 		break;
944*38905Sborman 	}
945*38905Sborman 
946*38905Sborman     }  /* end of case TELOPT_LINEMODE */
947*38905Sborman #endif
948*38905Sborman     case TELOPT_STATUS: {
949*38905Sborman 	int mode;
950*38905Sborman 
951*38905Sborman 	mode = SB_GET();
952*38905Sborman 	switch (mode) {
953*38905Sborman 	case TELQUAL_SEND:
954*38905Sborman 	    if (myopts[TELOPT_STATUS] == OPT_YES)
955*38905Sborman 		send_status();
956*38905Sborman 	    break;
957*38905Sborman 
958*38905Sborman 	case TELQUAL_IS:
959*38905Sborman 	    break;
960*38905Sborman 
961*38905Sborman 	default:
962*38905Sborman 	    break;
963*38905Sborman 	}
964*38905Sborman     }
965*38905Sborman 
966*38905Sborman     default:
967*38905Sborman 	break;
968*38905Sborman     }  /* end of switch */
969*38905Sborman 
970*38905Sborman }  /* end of suboption */
971*38905Sborman 
972*38905Sborman #define	ADD(c)	 *ncp++ = c;
973*38905Sborman #define	ADD_DATA(c) { *ncp++ = c; if (c == SE) *ncp++ = c; }
974*38905Sborman send_status()
975*38905Sborman {
976*38905Sborman 	char statusbuf[256];
977*38905Sborman 	register char *ncp;
978*38905Sborman 	register int i;
979*38905Sborman 
980*38905Sborman 	ncp = statusbuf;
981*38905Sborman 
982*38905Sborman 	netflush();	/* get rid of anything waiting to go out */
983*38905Sborman 
984*38905Sborman 	ADD(IAC);
985*38905Sborman 	ADD(SB);
986*38905Sborman 	ADD(TELOPT_STATUS);
987*38905Sborman 	ADD(TELQUAL_IS);
988*38905Sborman 
989*38905Sborman 	for (i = 0; i < NTELOPTS; i++) {
990*38905Sborman 		if (myopts[i] == OPT_YES) {
991*38905Sborman 			ADD(WILL);
992*38905Sborman 			ADD_DATA(i);
993*38905Sborman 			if (i == IAC)
994*38905Sborman 				ADD(IAC);
995*38905Sborman 		}
996*38905Sborman 		if (hisopts[i] == OPT_YES) {
997*38905Sborman 			ADD(DO);
998*38905Sborman 			ADD_DATA(i);
999*38905Sborman 			if (i == IAC)
1000*38905Sborman 				ADD(IAC);
1001*38905Sborman 		}
1002*38905Sborman 	}
1003*38905Sborman 
1004*38905Sborman #ifdef	LINEMODE
1005*38905Sborman 	if (hisopts[TELOPT_LINEMODE] == OPT_YES) {
1006*38905Sborman 		char *cp, *cpe;
1007*38905Sborman 		int len;
1008*38905Sborman 
1009*38905Sborman 		ADD(SB);
1010*38905Sborman 		ADD(TELOPT_LINEMODE);
1011*38905Sborman 		ADD(LM_MODE);
1012*38905Sborman 		ADD_DATA(editmode);
1013*38905Sborman 		if (editmode == IAC)
1014*38905Sborman 			ADD(IAC);
1015*38905Sborman 		ADD(SE);
1016*38905Sborman 
1017*38905Sborman 		ADD(SB);
1018*38905Sborman 		ADD(TELOPT_LINEMODE);
1019*38905Sborman 		ADD(LM_SLC);
1020*38905Sborman 		start_slc(0);
1021*38905Sborman 		send_slc();
1022*38905Sborman 		len = end_slc(&cp);
1023*38905Sborman 		for (cpe = cp + len; cp < cpe; cp++)
1024*38905Sborman 			ADD_DATA(*cp);
1025*38905Sborman 		ADD(SE);
1026*38905Sborman 	}
1027*38905Sborman #endif	/* LINEMODE */
1028*38905Sborman 
1029*38905Sborman 	ADD(IAC);
1030*38905Sborman 	ADD(SE);
1031*38905Sborman 
1032*38905Sborman 	writenet(statusbuf, ncp - statusbuf);
1033*38905Sborman 	netflush();	/* Send it on its way */
1034*38905Sborman }
1035