xref: /dragonfly/libexec/telnetd/state.c (revision 2cd2d2b5)
1 /*
2  * Copyright (c) 1989, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * @(#)state.c	8.5 (Berkeley) 5/30/95
34  * $FreeBSD: src/libexec/telnetd/state.c,v 1.9.2.4 2002/04/13 11:07:12 markm Exp $
35  * $DragonFly: src/libexec/telnetd/state.c,v 1.2 2003/06/17 04:27:08 dillon Exp $
36  */
37 
38 #include <stdarg.h>
39 #include "telnetd.h"
40 
41 unsigned char	doopt[] = { IAC, DO, '%', 'c', 0 };
42 unsigned char	dont[] = { IAC, DONT, '%', 'c', 0 };
43 unsigned char	will[] = { IAC, WILL, '%', 'c', 0 };
44 unsigned char	wont[] = { IAC, WONT, '%', 'c', 0 };
45 int	not42 = 1;
46 
47 /*
48  * Buffer for sub-options, and macros
49  * for suboptions buffer manipulations
50  */
51 unsigned char subbuffer[512], *subpointer= subbuffer, *subend= subbuffer;
52 
53 #define	SB_CLEAR()	subpointer = subbuffer
54 #define	SB_TERM()	{ subend = subpointer; SB_CLEAR(); }
55 #define	SB_ACCUM(c)	if (subpointer < (subbuffer+sizeof subbuffer)) { \
56 				*subpointer++ = (c); \
57 			}
58 #define	SB_GET()	((*subpointer++)&0xff)
59 #define	SB_EOF()	(subpointer >= subend)
60 #define	SB_LEN()	(subend - subpointer)
61 
62 #ifdef	ENV_HACK
63 unsigned char *subsave;
64 #define SB_SAVE()	subsave = subpointer;
65 #define	SB_RESTORE()	subpointer = subsave;
66 #endif
67 
68 
69 /*
70  * State for recv fsm
71  */
72 #define	TS_DATA		0	/* base state */
73 #define	TS_IAC		1	/* look for double IAC's */
74 #define	TS_CR		2	/* CR-LF ->'s CR */
75 #define	TS_SB		3	/* throw away begin's... */
76 #define	TS_SE		4	/* ...end's (suboption negotiation) */
77 #define	TS_WILL		5	/* will option negotiation */
78 #define	TS_WONT		6	/* wont " */
79 #define	TS_DO		7	/* do " */
80 #define	TS_DONT		8	/* dont " */
81 
82 static void doclientstat(void);
83 
84 void
85 telrcv(void)
86 {
87 	int c;
88 	static int state = TS_DATA;
89 
90 	while (ncc > 0) {
91 		if ((&ptyobuf[BUFSIZ] - pfrontp) < 2)
92 			break;
93 		c = *netip++ & 0377, ncc--;
94 		switch (state) {
95 
96 		case TS_CR:
97 			state = TS_DATA;
98 			/* Strip off \n or \0 after a \r */
99 			if ((c == 0) || (c == '\n')) {
100 				break;
101 			}
102 			/* FALL THROUGH */
103 
104 		case TS_DATA:
105 			if (c == IAC) {
106 				state = TS_IAC;
107 				break;
108 			}
109 			/*
110 			 * We now map \r\n ==> \r for pragmatic reasons.
111 			 * Many client implementations send \r\n when
112 			 * the user hits the CarriageReturn key.
113 			 *
114 			 * We USED to map \r\n ==> \n, since \r\n says
115 			 * that we want to be in column 1 of the next
116 			 * printable line, and \n is the standard
117 			 * unix way of saying that (\r is only good
118 			 * if CRMOD is set, which it normally is).
119 			 */
120 			if ((c == '\r') && his_state_is_wont(TELOPT_BINARY)) {
121 				int nc = *netip;
122 #ifdef	LINEMODE
123 				/*
124 				 * If we are operating in linemode,
125 				 * convert to local end-of-line.
126 				 */
127 				if (linemode && (ncc > 0) && (('\n' == nc) ||
128 					 ((0 == nc) && tty_iscrnl())) ) {
129 					netip++; ncc--;
130 					c = '\n';
131 				} else
132 #endif
133 				{
134 					state = TS_CR;
135 				}
136 			}
137 			*pfrontp++ = c;
138 			break;
139 
140 		case TS_IAC:
141 gotiac:			switch (c) {
142 
143 			/*
144 			 * Send the process on the pty side an
145 			 * interrupt.  Do this with a NULL or
146 			 * interrupt char; depending on the tty mode.
147 			 */
148 			case IP:
149 				DIAG(TD_OPTIONS,
150 					printoption("td: recv IAC", c));
151 				interrupt();
152 				break;
153 
154 			case BREAK:
155 				DIAG(TD_OPTIONS,
156 					printoption("td: recv IAC", c));
157 				sendbrk();
158 				break;
159 
160 			/*
161 			 * Are You There?
162 			 */
163 			case AYT:
164 				DIAG(TD_OPTIONS,
165 					printoption("td: recv IAC", c));
166 				recv_ayt();
167 				break;
168 
169 			/*
170 			 * Abort Output
171 			 */
172 			case AO:
173 			    {
174 				DIAG(TD_OPTIONS,
175 					printoption("td: recv IAC", c));
176 				ptyflush();	/* half-hearted */
177 				init_termbuf();
178 
179 				if (slctab[SLC_AO].sptr &&
180 				    *slctab[SLC_AO].sptr != (cc_t)(_POSIX_VDISABLE)) {
181 				    *pfrontp++ =
182 					(unsigned char)*slctab[SLC_AO].sptr;
183 				}
184 
185 				netclear();	/* clear buffer back */
186 				output_data("%c%c", IAC, DM);
187 				neturg = nfrontp-1; /* off by one XXX */
188 				DIAG(TD_OPTIONS,
189 					printoption("td: send IAC", DM));
190 				break;
191 			    }
192 
193 			/*
194 			 * Erase Character and
195 			 * Erase Line
196 			 */
197 			case EC:
198 			case EL:
199 			    {
200 				cc_t ch;
201 
202 				DIAG(TD_OPTIONS,
203 					printoption("td: recv IAC", c));
204 				ptyflush();	/* half-hearted */
205 				init_termbuf();
206 				if (c == EC)
207 					ch = *slctab[SLC_EC].sptr;
208 				else
209 					ch = *slctab[SLC_EL].sptr;
210 				if (ch != (cc_t)(_POSIX_VDISABLE))
211 					*pfrontp++ = (unsigned char)ch;
212 				break;
213 			    }
214 
215 			/*
216 			 * Check for urgent data...
217 			 */
218 			case DM:
219 				DIAG(TD_OPTIONS,
220 					printoption("td: recv IAC", c));
221 				SYNCHing = stilloob(net);
222 				settimer(gotDM);
223 				break;
224 
225 
226 			/*
227 			 * Begin option subnegotiation...
228 			 */
229 			case SB:
230 				state = TS_SB;
231 				SB_CLEAR();
232 				continue;
233 
234 			case WILL:
235 				state = TS_WILL;
236 				continue;
237 
238 			case WONT:
239 				state = TS_WONT;
240 				continue;
241 
242 			case DO:
243 				state = TS_DO;
244 				continue;
245 
246 			case DONT:
247 				state = TS_DONT;
248 				continue;
249 			case EOR:
250 				if (his_state_is_will(TELOPT_EOR))
251 					doeof();
252 				break;
253 
254 			/*
255 			 * Handle RFC 10xx Telnet linemode option additions
256 			 * to command stream (EOF, SUSP, ABORT).
257 			 */
258 			case xEOF:
259 				doeof();
260 				break;
261 
262 			case SUSP:
263 				sendsusp();
264 				break;
265 
266 			case ABORT:
267 				sendbrk();
268 				break;
269 
270 			case IAC:
271 				*pfrontp++ = c;
272 				break;
273 			}
274 			state = TS_DATA;
275 			break;
276 
277 		case TS_SB:
278 			if (c == IAC) {
279 				state = TS_SE;
280 			} else {
281 				SB_ACCUM(c);
282 			}
283 			break;
284 
285 		case TS_SE:
286 			if (c != SE) {
287 				if (c != IAC) {
288 					/*
289 					 * bad form of suboption negotiation.
290 					 * handle it in such a way as to avoid
291 					 * damage to local state.  Parse
292 					 * suboption buffer found so far,
293 					 * then treat remaining stream as
294 					 * another command sequence.
295 					 */
296 
297 					/* for DIAGNOSTICS */
298 					SB_ACCUM(IAC);
299 					SB_ACCUM(c);
300 					subpointer -= 2;
301 
302 					SB_TERM();
303 					suboption();
304 					state = TS_IAC;
305 					goto gotiac;
306 				}
307 				SB_ACCUM(c);
308 				state = TS_SB;
309 			} else {
310 				/* for DIAGNOSTICS */
311 				SB_ACCUM(IAC);
312 				SB_ACCUM(SE);
313 				subpointer -= 2;
314 
315 				SB_TERM();
316 				suboption();	/* handle sub-option */
317 				state = TS_DATA;
318 			}
319 			break;
320 
321 		case TS_WILL:
322 			willoption(c);
323 			state = TS_DATA;
324 			continue;
325 
326 		case TS_WONT:
327 			wontoption(c);
328 			state = TS_DATA;
329 			continue;
330 
331 		case TS_DO:
332 			dooption(c);
333 			state = TS_DATA;
334 			continue;
335 
336 		case TS_DONT:
337 			dontoption(c);
338 			state = TS_DATA;
339 			continue;
340 
341 		default:
342 			syslog(LOG_ERR, "panic state=%d", state);
343 			printf("telnetd: panic state=%d\n", state);
344 			exit(1);
345 		}
346 	}
347 }  /* end of telrcv */
348 
349 /*
350  * The will/wont/do/dont state machines are based on Dave Borman's
351  * Telnet option processing state machine.
352  *
353  * These correspond to the following states:
354  *	my_state = the last negotiated state
355  *	want_state = what I want the state to go to
356  *	want_resp = how many requests I have sent
357  * All state defaults are negative, and resp defaults to 0.
358  *
359  * When initiating a request to change state to new_state:
360  *
361  * if ((want_resp == 0 && new_state == my_state) || want_state == new_state) {
362  *	do nothing;
363  * } else {
364  *	want_state = new_state;
365  *	send new_state;
366  *	want_resp++;
367  * }
368  *
369  * When receiving new_state:
370  *
371  * if (want_resp) {
372  *	want_resp--;
373  *	if (want_resp && (new_state == my_state))
374  *		want_resp--;
375  * }
376  * if ((want_resp == 0) && (new_state != want_state)) {
377  *	if (ok_to_switch_to new_state)
378  *		want_state = new_state;
379  *	else
380  *		want_resp++;
381  *	send want_state;
382  * }
383  * my_state = new_state;
384  *
385  * Note that new_state is implied in these functions by the function itself.
386  * will and do imply positive new_state, wont and dont imply negative.
387  *
388  * Finally, there is one catch.  If we send a negative response to a
389  * positive request, my_state will be the positive while want_state will
390  * remain negative.  my_state will revert to negative when the negative
391  * acknowlegment arrives from the peer.  Thus, my_state generally tells
392  * us not only the last negotiated state, but also tells us what the peer
393  * wants to be doing as well.  It is important to understand this difference
394  * as we may wish to be processing data streams based on our desired state
395  * (want_state) or based on what the peer thinks the state is (my_state).
396  *
397  * This all works fine because if the peer sends a positive request, the data
398  * that we receive prior to negative acknowlegment will probably be affected
399  * by the positive state, and we can process it as such (if we can; if we
400  * can't then it really doesn't matter).  If it is that important, then the
401  * peer probably should be buffering until this option state negotiation
402  * is complete.
403  *
404  */
405 void
406 send_do(int option, int init)
407 {
408 	if (init) {
409 		if ((do_dont_resp[option] == 0 && his_state_is_will(option)) ||
410 		    his_want_state_is_will(option))
411 			return;
412 		/*
413 		 * Special case for TELOPT_TM:  We send a DO, but pretend
414 		 * that we sent a DONT, so that we can send more DOs if
415 		 * we want to.
416 		 */
417 		if (option == TELOPT_TM)
418 			set_his_want_state_wont(option);
419 		else
420 			set_his_want_state_will(option);
421 		do_dont_resp[option]++;
422 	}
423 	output_data((const char *)doopt, option);
424 
425 	DIAG(TD_OPTIONS, printoption("td: send do", option));
426 }
427 
428 void
429 willoption(int option)
430 {
431 	int changeok = 0;
432 	void (*func)(void) = 0;
433 
434 	/*
435 	 * process input from peer.
436 	 */
437 
438 	DIAG(TD_OPTIONS, printoption("td: recv will", option));
439 
440 	if (do_dont_resp[option]) {
441 		do_dont_resp[option]--;
442 		if (do_dont_resp[option] && his_state_is_will(option))
443 			do_dont_resp[option]--;
444 	}
445 	if (do_dont_resp[option] == 0) {
446 	    if (his_want_state_is_wont(option)) {
447 		switch (option) {
448 
449 		case TELOPT_BINARY:
450 			init_termbuf();
451 			tty_binaryin(1);
452 			set_termbuf();
453 			changeok++;
454 			break;
455 
456 		case TELOPT_ECHO:
457 			/*
458 			 * See comments below for more info.
459 			 */
460 			not42 = 0;	/* looks like a 4.2 system */
461 			break;
462 
463 		case TELOPT_TM:
464 #if	defined(LINEMODE) && defined(KLUDGELINEMODE)
465 			/*
466 			 * This telnetd implementation does not really
467 			 * support timing marks, it just uses them to
468 			 * support the kludge linemode stuff.  If we
469 			 * receive a will or wont TM in response to our
470 			 * do TM request that may have been sent to
471 			 * determine kludge linemode support, process
472 			 * it, otherwise TM should get a negative
473 			 * response back.
474 			 */
475 			/*
476 			 * Handle the linemode kludge stuff.
477 			 * If we are not currently supporting any
478 			 * linemode at all, then we assume that this
479 			 * is the client telling us to use kludge
480 			 * linemode in response to our query.  Set the
481 			 * linemode type that is to be supported, note
482 			 * that the client wishes to use linemode, and
483 			 * eat the will TM as though it never arrived.
484 			 */
485 			if (lmodetype < KLUDGE_LINEMODE) {
486 				lmodetype = KLUDGE_LINEMODE;
487 				clientstat(TELOPT_LINEMODE, WILL, 0);
488 				send_wont(TELOPT_SGA, 1);
489 			} else if (lmodetype == NO_AUTOKLUDGE) {
490 				lmodetype = KLUDGE_OK;
491 			}
492 #endif	/* defined(LINEMODE) && defined(KLUDGELINEMODE) */
493 			/*
494 			 * We never respond to a WILL TM, and
495 			 * we leave the state WONT.
496 			 */
497 			return;
498 
499 		case TELOPT_LFLOW:
500 			/*
501 			 * If we are going to support flow control
502 			 * option, then don't worry peer that we can't
503 			 * change the flow control characters.
504 			 */
505 			slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS;
506 			slctab[SLC_XON].defset.flag |= SLC_DEFAULT;
507 			slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS;
508 			slctab[SLC_XOFF].defset.flag |= SLC_DEFAULT;
509 		case TELOPT_TTYPE:
510 		case TELOPT_SGA:
511 		case TELOPT_NAWS:
512 		case TELOPT_TSPEED:
513 		case TELOPT_XDISPLOC:
514 		case TELOPT_NEW_ENVIRON:
515 		case TELOPT_OLD_ENVIRON:
516 			changeok++;
517 			break;
518 
519 #ifdef	LINEMODE
520 		case TELOPT_LINEMODE:
521 # ifdef	KLUDGELINEMODE
522 			/*
523 			 * Note client's desire to use linemode.
524 			 */
525 			lmodetype = REAL_LINEMODE;
526 # endif	/* KLUDGELINEMODE */
527 			func = doclientstat;
528 			changeok++;
529 			break;
530 #endif	/* LINEMODE */
531 
532 
533 
534 		default:
535 			break;
536 		}
537 		if (changeok) {
538 			set_his_want_state_will(option);
539 			send_do(option, 0);
540 		} else {
541 			do_dont_resp[option]++;
542 			send_dont(option, 0);
543 		}
544 	    } else {
545 		/*
546 		 * Option processing that should happen when
547 		 * we receive conformation of a change in
548 		 * state that we had requested.
549 		 */
550 		switch (option) {
551 		case TELOPT_ECHO:
552 			not42 = 0;	/* looks like a 4.2 system */
553 			/*
554 			 * Egads, he responded "WILL ECHO".  Turn
555 			 * it off right now!
556 			 */
557 			send_dont(option, 1);
558 			/*
559 			 * "WILL ECHO".  Kludge upon kludge!
560 			 * A 4.2 client is now echoing user input at
561 			 * the tty.  This is probably undesireable and
562 			 * it should be stopped.  The client will
563 			 * respond WONT TM to the DO TM that we send to
564 			 * check for kludge linemode.  When the WONT TM
565 			 * arrives, linemode will be turned off and a
566 			 * change propogated to the pty.  This change
567 			 * will cause us to process the new pty state
568 			 * in localstat(), which will notice that
569 			 * linemode is off and send a WILL ECHO
570 			 * so that we are properly in character mode and
571 			 * all is well.
572 			 */
573 			break;
574 #ifdef	LINEMODE
575 		case TELOPT_LINEMODE:
576 # ifdef	KLUDGELINEMODE
577 			/*
578 			 * Note client's desire to use linemode.
579 			 */
580 			lmodetype = REAL_LINEMODE;
581 # endif	/* KLUDGELINEMODE */
582 			func = doclientstat;
583 			break;
584 #endif	/* LINEMODE */
585 
586 
587 		case TELOPT_LFLOW:
588 			func = flowstat;
589 			break;
590 		}
591 	    }
592 	}
593 	set_his_state_will(option);
594 	if (func)
595 		(*func)();
596 }  /* end of willoption */
597 
598 void
599 send_dont(int option, int init)
600 {
601 	if (init) {
602 		if ((do_dont_resp[option] == 0 && his_state_is_wont(option)) ||
603 		    his_want_state_is_wont(option))
604 			return;
605 		set_his_want_state_wont(option);
606 		do_dont_resp[option]++;
607 	}
608 	output_data((const char *)dont, option);
609 
610 	DIAG(TD_OPTIONS, printoption("td: send dont", option));
611 }
612 
613 void
614 wontoption(int option)
615 {
616 	/*
617 	 * Process client input.
618 	 */
619 
620 	DIAG(TD_OPTIONS, printoption("td: recv wont", option));
621 
622 	if (do_dont_resp[option]) {
623 		do_dont_resp[option]--;
624 		if (do_dont_resp[option] && his_state_is_wont(option))
625 			do_dont_resp[option]--;
626 	}
627 	if (do_dont_resp[option] == 0) {
628 	    if (his_want_state_is_will(option)) {
629 		/* it is always ok to change to negative state */
630 		switch (option) {
631 		case TELOPT_ECHO:
632 			not42 = 1; /* doesn't seem to be a 4.2 system */
633 			break;
634 
635 		case TELOPT_BINARY:
636 			init_termbuf();
637 			tty_binaryin(0);
638 			set_termbuf();
639 			break;
640 
641 #ifdef	LINEMODE
642 		case TELOPT_LINEMODE:
643 # ifdef	KLUDGELINEMODE
644 			/*
645 			 * If real linemode is supported, then client is
646 			 * asking to turn linemode off.
647 			 */
648 			if (lmodetype != REAL_LINEMODE)
649 				break;
650 			lmodetype = KLUDGE_LINEMODE;
651 # endif	/* KLUDGELINEMODE */
652 			clientstat(TELOPT_LINEMODE, WONT, 0);
653 			break;
654 #endif	/* LINEMODE */
655 
656 		case TELOPT_TM:
657 			/*
658 			 * If we get a WONT TM, and had sent a DO TM,
659 			 * don't respond with a DONT TM, just leave it
660 			 * as is.  Short circut the state machine to
661 			 * achive this.
662 			 */
663 			set_his_want_state_wont(TELOPT_TM);
664 			return;
665 
666 		case TELOPT_LFLOW:
667 			/*
668 			 * If we are not going to support flow control
669 			 * option, then let peer know that we can't
670 			 * change the flow control characters.
671 			 */
672 			slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS;
673 			slctab[SLC_XON].defset.flag |= SLC_CANTCHANGE;
674 			slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS;
675 			slctab[SLC_XOFF].defset.flag |= SLC_CANTCHANGE;
676 			break;
677 
678 
679 		/*
680 		 * For options that we might spin waiting for
681 		 * sub-negotiation, if the client turns off the
682 		 * option rather than responding to the request,
683 		 * we have to treat it here as if we got a response
684 		 * to the sub-negotiation, (by updating the timers)
685 		 * so that we'll break out of the loop.
686 		 */
687 		case TELOPT_TTYPE:
688 			settimer(ttypesubopt);
689 			break;
690 
691 		case TELOPT_TSPEED:
692 			settimer(tspeedsubopt);
693 			break;
694 
695 		case TELOPT_XDISPLOC:
696 			settimer(xdisplocsubopt);
697 			break;
698 
699 		case TELOPT_OLD_ENVIRON:
700 			settimer(oenvironsubopt);
701 			break;
702 
703 		case TELOPT_NEW_ENVIRON:
704 			settimer(environsubopt);
705 			break;
706 
707 		default:
708 			break;
709 		}
710 		set_his_want_state_wont(option);
711 		if (his_state_is_will(option))
712 			send_dont(option, 0);
713 	    } else {
714 		switch (option) {
715 		case TELOPT_TM:
716 #if	defined(LINEMODE) && defined(KLUDGELINEMODE)
717 			if (lmodetype < NO_AUTOKLUDGE) {
718 				lmodetype = NO_LINEMODE;
719 				clientstat(TELOPT_LINEMODE, WONT, 0);
720 				send_will(TELOPT_SGA, 1);
721 				send_will(TELOPT_ECHO, 1);
722 			}
723 #endif	/* defined(LINEMODE) && defined(KLUDGELINEMODE) */
724 			break;
725 
726 		default:
727 			break;
728 		}
729 	    }
730 	}
731 	set_his_state_wont(option);
732 
733 }  /* end of wontoption */
734 
735 void
736 send_will(int option, int init)
737 {
738 	if (init) {
739 		if ((will_wont_resp[option] == 0 && my_state_is_will(option))||
740 		    my_want_state_is_will(option))
741 			return;
742 		set_my_want_state_will(option);
743 		will_wont_resp[option]++;
744 	}
745 	output_data((const char *)will, option);
746 
747 	DIAG(TD_OPTIONS, printoption("td: send will", option));
748 }
749 
750 #if	!defined(LINEMODE) || !defined(KLUDGELINEMODE)
751 /*
752  * When we get a DONT SGA, we will try once to turn it
753  * back on.  If the other side responds DONT SGA, we
754  * leave it at that.  This is so that when we talk to
755  * clients that understand KLUDGELINEMODE but not LINEMODE,
756  * we'll keep them in char-at-a-time mode.
757  */
758 int turn_on_sga = 0;
759 #endif
760 
761 void
762 dooption(int option)
763 {
764 	int changeok = 0;
765 
766 	/*
767 	 * Process client input.
768 	 */
769 
770 	DIAG(TD_OPTIONS, printoption("td: recv do", option));
771 
772 	if (will_wont_resp[option]) {
773 		will_wont_resp[option]--;
774 		if (will_wont_resp[option] && my_state_is_will(option))
775 			will_wont_resp[option]--;
776 	}
777 	if ((will_wont_resp[option] == 0) && (my_want_state_is_wont(option))) {
778 		switch (option) {
779 		case TELOPT_ECHO:
780 #ifdef	LINEMODE
781 # ifdef	KLUDGELINEMODE
782 			if (lmodetype == NO_LINEMODE)
783 # else
784 			if (his_state_is_wont(TELOPT_LINEMODE))
785 # endif
786 #endif
787 			{
788 				init_termbuf();
789 				tty_setecho(1);
790 				set_termbuf();
791 			}
792 			changeok++;
793 			break;
794 
795 		case TELOPT_BINARY:
796 			init_termbuf();
797 			tty_binaryout(1);
798 			set_termbuf();
799 			changeok++;
800 			break;
801 
802 		case TELOPT_SGA:
803 #if	defined(LINEMODE) && defined(KLUDGELINEMODE)
804 			/*
805 			 * If kludge linemode is in use, then we must
806 			 * process an incoming do SGA for linemode
807 			 * purposes.
808 			 */
809 			if (lmodetype == KLUDGE_LINEMODE) {
810 				/*
811 				 * Receipt of "do SGA" in kludge
812 				 * linemode is the peer asking us to
813 				 * turn off linemode.  Make note of
814 				 * the request.
815 				 */
816 				clientstat(TELOPT_LINEMODE, WONT, 0);
817 				/*
818 				 * If linemode did not get turned off
819 				 * then don't tell peer that we did.
820 				 * Breaking here forces a wont SGA to
821 				 * be returned.
822 				 */
823 				if (linemode)
824 					break;
825 			}
826 #else
827 			turn_on_sga = 0;
828 #endif	/* defined(LINEMODE) && defined(KLUDGELINEMODE) */
829 			changeok++;
830 			break;
831 
832 		case TELOPT_STATUS:
833 			changeok++;
834 			break;
835 
836 		case TELOPT_TM:
837 			/*
838 			 * Special case for TM.  We send a WILL, but
839 			 * pretend we sent a WONT.
840 			 */
841 			send_will(option, 0);
842 			set_my_want_state_wont(option);
843 			set_my_state_wont(option);
844 			return;
845 
846 		case TELOPT_LOGOUT:
847 			/*
848 			 * When we get a LOGOUT option, respond
849 			 * with a WILL LOGOUT, make sure that
850 			 * it gets written out to the network,
851 			 * and then just go away...
852 			 */
853 			set_my_want_state_will(TELOPT_LOGOUT);
854 			send_will(TELOPT_LOGOUT, 0);
855 			set_my_state_will(TELOPT_LOGOUT);
856 			(void)netflush();
857 			cleanup(0);
858 			/* NOT REACHED */
859 			break;
860 
861 		case TELOPT_LINEMODE:
862 		case TELOPT_TTYPE:
863 		case TELOPT_NAWS:
864 		case TELOPT_TSPEED:
865 		case TELOPT_LFLOW:
866 		case TELOPT_XDISPLOC:
867 #ifdef	TELOPT_ENVIRON
868 		case TELOPT_NEW_ENVIRON:
869 #endif
870 		case TELOPT_OLD_ENVIRON:
871 		default:
872 			break;
873 		}
874 		if (changeok) {
875 			set_my_want_state_will(option);
876 			send_will(option, 0);
877 		} else {
878 			will_wont_resp[option]++;
879 			send_wont(option, 0);
880 		}
881 	}
882 	set_my_state_will(option);
883 
884 }  /* end of dooption */
885 
886 void
887 send_wont(int option, int init)
888 {
889 	if (init) {
890 		if ((will_wont_resp[option] == 0 && my_state_is_wont(option)) ||
891 		    my_want_state_is_wont(option))
892 			return;
893 		set_my_want_state_wont(option);
894 		will_wont_resp[option]++;
895 	}
896 	output_data((const char *)wont, option);
897 
898 	DIAG(TD_OPTIONS, printoption("td: send wont", option));
899 }
900 
901 void
902 dontoption(int option)
903 {
904 	/*
905 	 * Process client input.
906 	 */
907 
908 
909 	DIAG(TD_OPTIONS, printoption("td: recv dont", option));
910 
911 	if (will_wont_resp[option]) {
912 		will_wont_resp[option]--;
913 		if (will_wont_resp[option] && my_state_is_wont(option))
914 			will_wont_resp[option]--;
915 	}
916 	if ((will_wont_resp[option] == 0) && (my_want_state_is_will(option))) {
917 		switch (option) {
918 		case TELOPT_BINARY:
919 			init_termbuf();
920 			tty_binaryout(0);
921 			set_termbuf();
922 			break;
923 
924 		case TELOPT_ECHO:	/* we should stop echoing */
925 #ifdef	LINEMODE
926 # ifdef	KLUDGELINEMODE
927 			if ((lmodetype != REAL_LINEMODE) &&
928 			    (lmodetype != KLUDGE_LINEMODE))
929 # else
930 			if (his_state_is_wont(TELOPT_LINEMODE))
931 # endif
932 #endif
933 			{
934 				init_termbuf();
935 				tty_setecho(0);
936 				set_termbuf();
937 			}
938 			break;
939 
940 		case TELOPT_SGA:
941 #if	defined(LINEMODE) && defined(KLUDGELINEMODE)
942 			/*
943 			 * If kludge linemode is in use, then we
944 			 * must process an incoming do SGA for
945 			 * linemode purposes.
946 			 */
947 			if ((lmodetype == KLUDGE_LINEMODE) ||
948 			    (lmodetype == KLUDGE_OK)) {
949 				/*
950 				 * The client is asking us to turn
951 				 * linemode on.
952 				 */
953 				lmodetype = KLUDGE_LINEMODE;
954 				clientstat(TELOPT_LINEMODE, WILL, 0);
955 				/*
956 				 * If we did not turn line mode on,
957 				 * then what do we say?  Will SGA?
958 				 * This violates design of telnet.
959 				 * Gross.  Very Gross.
960 				 */
961 			}
962 			break;
963 #else
964 			set_my_want_state_wont(option);
965 			if (my_state_is_will(option))
966 				send_wont(option, 0);
967 			set_my_state_wont(option);
968 			if (turn_on_sga ^= 1)
969 				send_will(option, 1);
970 			return;
971 #endif	/* defined(LINEMODE) && defined(KLUDGELINEMODE) */
972 
973 		default:
974 			break;
975 		}
976 
977 		set_my_want_state_wont(option);
978 		if (my_state_is_will(option))
979 			send_wont(option, 0);
980 	}
981 	set_my_state_wont(option);
982 
983 }  /* end of dontoption */
984 
985 #ifdef	ENV_HACK
986 int env_ovar = -1;
987 int env_ovalue = -1;
988 #else	/* ENV_HACK */
989 # define env_ovar OLD_ENV_VAR
990 # define env_ovalue OLD_ENV_VALUE
991 #endif	/* ENV_HACK */
992 
993 /*
994  * suboption()
995  *
996  *	Look at the sub-option buffer, and try to be helpful to the other
997  * side.
998  *
999  *	Currently we recognize:
1000  *
1001  *	Terminal type is
1002  *	Linemode
1003  *	Window size
1004  *	Terminal speed
1005  */
1006 void
1007 suboption(void)
1008 {
1009     int subchar;
1010 
1011     DIAG(TD_OPTIONS, {netflush(); printsub('<', subpointer, SB_LEN()+2);});
1012 
1013     subchar = SB_GET();
1014     switch (subchar) {
1015     case TELOPT_TSPEED: {
1016 	int xspeed, rspeed;
1017 
1018 	if (his_state_is_wont(TELOPT_TSPEED))	/* Ignore if option disabled */
1019 		break;
1020 
1021 	settimer(tspeedsubopt);
1022 
1023 	if (SB_EOF() || SB_GET() != TELQUAL_IS)
1024 		return;
1025 
1026 	xspeed = atoi((char *)subpointer);
1027 
1028 	while (SB_GET() != ',' && !SB_EOF());
1029 	if (SB_EOF())
1030 		return;
1031 
1032 	rspeed = atoi((char *)subpointer);
1033 	clientstat(TELOPT_TSPEED, xspeed, rspeed);
1034 
1035 	break;
1036 
1037     }  /* end of case TELOPT_TSPEED */
1038 
1039     case TELOPT_TTYPE: {		/* Yaaaay! */
1040 	static char terminalname[41];
1041 
1042 	if (his_state_is_wont(TELOPT_TTYPE))	/* Ignore if option disabled */
1043 		break;
1044 	settimer(ttypesubopt);
1045 
1046 	if (SB_EOF() || SB_GET() != TELQUAL_IS) {
1047 	    return;		/* ??? XXX but, this is the most robust */
1048 	}
1049 
1050 	terminaltype = terminalname;
1051 
1052 	while ((terminaltype < (terminalname + sizeof terminalname-1)) &&
1053 								    !SB_EOF()) {
1054 	    int c;
1055 
1056 	    c = SB_GET();
1057 	    if (isupper(c)) {
1058 		c = tolower(c);
1059 	    }
1060 	    *terminaltype++ = c;    /* accumulate name */
1061 	}
1062 	*terminaltype = 0;
1063 	terminaltype = terminalname;
1064 	break;
1065     }  /* end of case TELOPT_TTYPE */
1066 
1067     case TELOPT_NAWS: {
1068 	int xwinsize, ywinsize;
1069 
1070 	if (his_state_is_wont(TELOPT_NAWS))	/* Ignore if option disabled */
1071 		break;
1072 
1073 	if (SB_EOF())
1074 		return;
1075 	xwinsize = SB_GET() << 8;
1076 	if (SB_EOF())
1077 		return;
1078 	xwinsize |= SB_GET();
1079 	if (SB_EOF())
1080 		return;
1081 	ywinsize = SB_GET() << 8;
1082 	if (SB_EOF())
1083 		return;
1084 	ywinsize |= SB_GET();
1085 	clientstat(TELOPT_NAWS, xwinsize, ywinsize);
1086 
1087 	break;
1088 
1089     }  /* end of case TELOPT_NAWS */
1090 
1091 #ifdef	LINEMODE
1092     case TELOPT_LINEMODE: {
1093 	int request;
1094 
1095 	if (his_state_is_wont(TELOPT_LINEMODE))	/* Ignore if option disabled */
1096 		break;
1097 	/*
1098 	 * Process linemode suboptions.
1099 	 */
1100 	if (SB_EOF())
1101 	    break;		/* garbage was sent */
1102 	request = SB_GET();	/* get will/wont */
1103 
1104 	if (SB_EOF())
1105 	    break;		/* another garbage check */
1106 
1107 	if (request == LM_SLC) {  /* SLC is not preceeded by WILL or WONT */
1108 		/*
1109 		 * Process suboption buffer of slc's
1110 		 */
1111 		start_slc(1);
1112 		do_opt_slc(subpointer, subend - subpointer);
1113 		(void) end_slc(0);
1114 		break;
1115 	} else if (request == LM_MODE) {
1116 		if (SB_EOF())
1117 		    return;
1118 		useeditmode = SB_GET();  /* get mode flag */
1119 		clientstat(LM_MODE, 0, 0);
1120 		break;
1121 	}
1122 
1123 	if (SB_EOF())
1124 	    break;
1125 	switch (SB_GET()) {  /* what suboption? */
1126 	case LM_FORWARDMASK:
1127 		/*
1128 		 * According to spec, only server can send request for
1129 		 * forwardmask, and client can only return a positive response.
1130 		 * So don't worry about it.
1131 		 */
1132 
1133 	default:
1134 		break;
1135 	}
1136 	break;
1137     }  /* end of case TELOPT_LINEMODE */
1138 #endif
1139     case TELOPT_STATUS: {
1140 	int mode;
1141 
1142 	if (SB_EOF())
1143 	    break;
1144 	mode = SB_GET();
1145 	switch (mode) {
1146 	case TELQUAL_SEND:
1147 	    if (my_state_is_will(TELOPT_STATUS))
1148 		send_status();
1149 	    break;
1150 
1151 	case TELQUAL_IS:
1152 	    break;
1153 
1154 	default:
1155 	    break;
1156 	}
1157 	break;
1158     }  /* end of case TELOPT_STATUS */
1159 
1160     case TELOPT_XDISPLOC: {
1161 	if (SB_EOF() || SB_GET() != TELQUAL_IS)
1162 		return;
1163 	settimer(xdisplocsubopt);
1164 	subpointer[SB_LEN()] = '\0';
1165 	(void)setenv("DISPLAY", (char *)subpointer, 1);
1166 	break;
1167     }  /* end of case TELOPT_XDISPLOC */
1168 
1169 #ifdef	TELOPT_NEW_ENVIRON
1170     case TELOPT_NEW_ENVIRON:
1171 #endif
1172     case TELOPT_OLD_ENVIRON: {
1173 	int c;
1174 	char *cp, *varp, *valp;
1175 
1176 	if (SB_EOF())
1177 		return;
1178 	c = SB_GET();
1179 	if (c == TELQUAL_IS) {
1180 		if (subchar == TELOPT_OLD_ENVIRON)
1181 			settimer(oenvironsubopt);
1182 		else
1183 			settimer(environsubopt);
1184 	} else if (c != TELQUAL_INFO) {
1185 		return;
1186 	}
1187 
1188 #ifdef	TELOPT_NEW_ENVIRON
1189 	if (subchar == TELOPT_NEW_ENVIRON) {
1190 	    while (!SB_EOF()) {
1191 		c = SB_GET();
1192 		if ((c == NEW_ENV_VAR) || (c == ENV_USERVAR))
1193 			break;
1194 	    }
1195 	} else
1196 #endif
1197 	{
1198 #ifdef	ENV_HACK
1199 	    /*
1200 	     * We only want to do this if we haven't already decided
1201 	     * whether or not the other side has its VALUE and VAR
1202 	     * reversed.
1203 	     */
1204 	    if (env_ovar < 0) {
1205 		int last = -1;		/* invalid value */
1206 		int empty = 0;
1207 		int got_var = 0, got_value = 0, got_uservar = 0;
1208 
1209 		/*
1210 		 * The other side might have its VALUE and VAR values
1211 		 * reversed.  To be interoperable, we need to determine
1212 		 * which way it is.  If the first recognized character
1213 		 * is a VAR or VALUE, then that will tell us what
1214 		 * type of client it is.  If the fist recognized
1215 		 * character is a USERVAR, then we continue scanning
1216 		 * the suboption looking for two consecutive
1217 		 * VAR or VALUE fields.  We should not get two
1218 		 * consecutive VALUE fields, so finding two
1219 		 * consecutive VALUE or VAR fields will tell us
1220 		 * what the client is.
1221 		 */
1222 		SB_SAVE();
1223 		while (!SB_EOF()) {
1224 			c = SB_GET();
1225 			switch(c) {
1226 			case OLD_ENV_VAR:
1227 				if (last < 0 || last == OLD_ENV_VAR
1228 				    || (empty && (last == OLD_ENV_VALUE)))
1229 					goto env_ovar_ok;
1230 				got_var++;
1231 				last = OLD_ENV_VAR;
1232 				break;
1233 			case OLD_ENV_VALUE:
1234 				if (last < 0 || last == OLD_ENV_VALUE
1235 				    || (empty && (last == OLD_ENV_VAR)))
1236 					goto env_ovar_wrong;
1237 				got_value++;
1238 				last = OLD_ENV_VALUE;
1239 				break;
1240 			case ENV_USERVAR:
1241 				/* count strings of USERVAR as one */
1242 				if (last != ENV_USERVAR)
1243 					got_uservar++;
1244 				if (empty) {
1245 					if (last == OLD_ENV_VALUE)
1246 						goto env_ovar_ok;
1247 					if (last == OLD_ENV_VAR)
1248 						goto env_ovar_wrong;
1249 				}
1250 				last = ENV_USERVAR;
1251 				break;
1252 			case ENV_ESC:
1253 				if (!SB_EOF())
1254 					c = SB_GET();
1255 				/* FALL THROUGH */
1256 			default:
1257 				empty = 0;
1258 				continue;
1259 			}
1260 			empty = 1;
1261 		}
1262 		if (empty) {
1263 			if (last == OLD_ENV_VALUE)
1264 				goto env_ovar_ok;
1265 			if (last == OLD_ENV_VAR)
1266 				goto env_ovar_wrong;
1267 		}
1268 		/*
1269 		 * Ok, the first thing was a USERVAR, and there
1270 		 * are not two consecutive VAR or VALUE commands,
1271 		 * and none of the VAR or VALUE commands are empty.
1272 		 * If the client has sent us a well-formed option,
1273 		 * then the number of VALUEs received should always
1274 		 * be less than or equal to the number of VARs and
1275 		 * USERVARs received.
1276 		 *
1277 		 * If we got exactly as many VALUEs as VARs and
1278 		 * USERVARs, the client has the same definitions.
1279 		 *
1280 		 * If we got exactly as many VARs as VALUEs and
1281 		 * USERVARS, the client has reversed definitions.
1282 		 */
1283 		if (got_uservar + got_var == got_value) {
1284 	    env_ovar_ok:
1285 			env_ovar = OLD_ENV_VAR;
1286 			env_ovalue = OLD_ENV_VALUE;
1287 		} else if (got_uservar + got_value == got_var) {
1288 	    env_ovar_wrong:
1289 			env_ovar = OLD_ENV_VALUE;
1290 			env_ovalue = OLD_ENV_VAR;
1291 			DIAG(TD_OPTIONS,
1292 			    output_data("ENVIRON VALUE and VAR are reversed!\r\n"));
1293 
1294 		}
1295 	    }
1296 	    SB_RESTORE();
1297 #endif
1298 
1299 	    while (!SB_EOF()) {
1300 		c = SB_GET();
1301 		if ((c == env_ovar) || (c == ENV_USERVAR))
1302 			break;
1303 	    }
1304 	}
1305 
1306 	if (SB_EOF())
1307 		return;
1308 
1309 	cp = varp = (char *)subpointer;
1310 	valp = 0;
1311 
1312 	while (!SB_EOF()) {
1313 		c = SB_GET();
1314 		if (subchar == TELOPT_OLD_ENVIRON) {
1315 			if (c == env_ovar)
1316 				c = NEW_ENV_VAR;
1317 			else if (c == env_ovalue)
1318 				c = NEW_ENV_VALUE;
1319 		}
1320 		switch (c) {
1321 
1322 		case NEW_ENV_VALUE:
1323 			*cp = '\0';
1324 			cp = valp = (char *)subpointer;
1325 			break;
1326 
1327 		case NEW_ENV_VAR:
1328 		case ENV_USERVAR:
1329 			*cp = '\0';
1330 			if (valp)
1331 				(void)setenv(varp, valp, 1);
1332 			else
1333 				unsetenv(varp);
1334 			cp = varp = (char *)subpointer;
1335 			valp = 0;
1336 			break;
1337 
1338 		case ENV_ESC:
1339 			if (SB_EOF())
1340 				break;
1341 			c = SB_GET();
1342 			/* FALL THROUGH */
1343 		default:
1344 			*cp++ = c;
1345 			break;
1346 		}
1347 	}
1348 	*cp = '\0';
1349 	if (valp)
1350 		(void)setenv(varp, valp, 1);
1351 	else
1352 		unsetenv(varp);
1353 	break;
1354     }  /* end of case TELOPT_NEW_ENVIRON */
1355 
1356     default:
1357 	break;
1358     }  /* end of switch */
1359 
1360 }  /* end of suboption */
1361 
1362 static void
1363 doclientstat(void)
1364 {
1365 	clientstat(TELOPT_LINEMODE, WILL, 0);
1366 }
1367 
1368 #define	ADD(c)	 *ncp++ = c
1369 #define	ADD_DATA(c) { *ncp++ = c; if (c == SE || c == IAC) *ncp++ = c; }
1370 void
1371 send_status(void)
1372 {
1373 	unsigned char statusbuf[256];
1374 	unsigned char *ncp;
1375 	unsigned char i;
1376 
1377 	ncp = statusbuf;
1378 
1379 	netflush();	/* get rid of anything waiting to go out */
1380 
1381 	ADD(IAC);
1382 	ADD(SB);
1383 	ADD(TELOPT_STATUS);
1384 	ADD(TELQUAL_IS);
1385 
1386 	/*
1387 	 * We check the want_state rather than the current state,
1388 	 * because if we received a DO/WILL for an option that we
1389 	 * don't support, and the other side didn't send a DONT/WONT
1390 	 * in response to our WONT/DONT, then the "state" will be
1391 	 * WILL/DO, and the "want_state" will be WONT/DONT.  We
1392 	 * need to go by the latter.
1393 	 */
1394 	for (i = 0; i < (unsigned char)NTELOPTS; i++) {
1395 		if (my_want_state_is_will(i)) {
1396 			ADD(WILL);
1397 			ADD_DATA(i);
1398 			if (i == IAC)
1399 				ADD(IAC);
1400 		}
1401 		if (his_want_state_is_will(i)) {
1402 			ADD(DO);
1403 			ADD_DATA(i);
1404 			if (i == IAC)
1405 				ADD(IAC);
1406 		}
1407 	}
1408 
1409 	if (his_want_state_is_will(TELOPT_LFLOW)) {
1410 		ADD(SB);
1411 		ADD(TELOPT_LFLOW);
1412 		if (flowmode) {
1413 			ADD(LFLOW_ON);
1414 		} else {
1415 			ADD(LFLOW_OFF);
1416 		}
1417 		ADD(SE);
1418 
1419 		if (restartany >= 0) {
1420 			ADD(SB);
1421 			ADD(TELOPT_LFLOW);
1422 			if (restartany) {
1423 				ADD(LFLOW_RESTART_ANY);
1424 			} else {
1425 				ADD(LFLOW_RESTART_XON);
1426 			}
1427 			ADD(SE);
1428 		}
1429 	}
1430 
1431 #ifdef	LINEMODE
1432 	if (his_want_state_is_will(TELOPT_LINEMODE)) {
1433 		unsigned char *cp, *cpe;
1434 		int len;
1435 
1436 		ADD(SB);
1437 		ADD(TELOPT_LINEMODE);
1438 		ADD(LM_MODE);
1439 		ADD_DATA(editmode);
1440 		ADD(SE);
1441 
1442 		ADD(SB);
1443 		ADD(TELOPT_LINEMODE);
1444 		ADD(LM_SLC);
1445 		start_slc(0);
1446 		send_slc();
1447 		len = end_slc(&cp);
1448 		for (cpe = cp + len; cp < cpe; cp++)
1449 			ADD_DATA(*cp);
1450 		ADD(SE);
1451 	}
1452 #endif	/* LINEMODE */
1453 
1454 	ADD(IAC);
1455 	ADD(SE);
1456 
1457 	output_datalen(statusbuf, ncp - statusbuf);
1458 	netflush();	/* Send it on its way */
1459 
1460 	DIAG(TD_OPTIONS,
1461 		{printsub('>', statusbuf, ncp - statusbuf); netflush();});
1462 }
1463 
1464 /*
1465  * This function appends data to nfrontp and advances nfrontp.
1466  * Returns the number of characters written altogether (the
1467  * buffer may have been flushed in the process).
1468  */
1469 
1470 int
1471 output_data(const char *format, ...)
1472 {
1473 	va_list args;
1474 	int len;
1475 	char *buf;
1476 
1477 	va_start(args, format);
1478 	if ((len = vasprintf(&buf, format, args)) == -1)
1479 		return -1;
1480 	output_datalen(buf, len);
1481 	va_end(args);
1482 	free(buf);
1483 	return (len);
1484 }
1485 
1486 void
1487 output_datalen(const char *buf, int len)
1488 {
1489 	int remaining, copied;
1490 
1491 	remaining = BUFSIZ - (nfrontp - netobuf);
1492 	while (len > 0) {
1493 		/* Free up enough space if the room is too low*/
1494 		if ((len > BUFSIZ ? BUFSIZ : len) > remaining) {
1495 			netflush();
1496 			remaining = BUFSIZ - (nfrontp - netobuf);
1497 		}
1498 
1499 		/* Copy out as much as will fit */
1500 		copied = remaining > len ? len : remaining;
1501 		memmove(nfrontp, buf, copied);
1502 		nfrontp += copied;
1503 		len -= copied;
1504 		remaining -= copied;
1505 		buf += copied;
1506 	}
1507 	return;
1508 }
1509