xref: /original-bsd/usr.bin/telnet/telnet.c (revision 87febec0)
1 /*
2  * Copyright (c) 1988 Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * advertising materials, and other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  */
17 
18 #ifndef lint
19 static char sccsid[] = "@(#)telnet.c	5.40 (Berkeley) 03/21/89";
20 #endif /* not lint */
21 
22 #include <sys/types.h>
23 
24 #if	defined(unix)
25 #include <signal.h>
26 /* By the way, we need to include curses.h before telnet.h since,
27  * among other things, telnet.h #defines 'DO', which is a variable
28  * declared in curses.h.
29  */
30 #include <curses.h>
31 #endif	/* defined(unix) */
32 
33 #include <arpa/telnet.h>
34 
35 #if	defined(unix)
36 #include <strings.h>
37 #else	/* defined(unix) */
38 #include <string.h>
39 #endif	/* defined(unix) */
40 
41 #include "ring.h"
42 
43 #include "defines.h"
44 #include "externs.h"
45 #include "types.h"
46 #include "general.h"
47 
48 
49 #define	strip(x)	((x)&0x7f)
50 
51 
52 static char	subbuffer[SUBBUFSIZE],
53 		*subpointer, *subend;	 /* buffer for sub-options */
54 #define	SB_CLEAR()	subpointer = subbuffer;
55 #define	SB_TERM()	subend = subpointer;
56 #define	SB_ACCUM(c)	if (subpointer < (subbuffer+sizeof subbuffer)) { \
57 				*subpointer++ = (c); \
58 			}
59 
60 char	options[256];		/* The combined options */
61 #define	he_said_will(c)		options[c] |= OPT_HE_SAID_WILL
62 #define	he_said_wont(c)		options[c] &= ~OPT_HE_SAID_WILL
63 #define	he_said_do(c)		options[c] |= OPT_HE_SAID_DO
64 #define	he_said_dont(c)		options[c] &= ~OPT_HE_SAID_DO
65 
66 #define	I_said_will(c)		options[c] |= OPT_I_SAID_WILL
67 #define	I_said_wont(c)		options[c] &= ~OPT_I_SAID_WILL
68 #define	I_said_do(c)		options[c] |= OPT_I_SAID_DO
69 #define	I_said_dont(c)		options[c] &= ~OPT_I_SAID_DO
70 
71 int
72 	connected,
73 	showoptions,
74 	In3270,		/* Are we in 3270 mode? */
75 	ISend,		/* trying to send network data in */
76 	debug = 0,
77 	crmod,
78 	netdata,	/* Print out network data flow */
79 	crlf,		/* Should '\r' be mapped to <CR><LF> (or <CR><NUL>)? */
80 #if	defined(TN3270)
81 	noasynchtty = 0,/* User specified "-noasynch" on command line */
82 	noasynchnet = 0,/* User specified "-noasynch" on command line */
83 	askedSGA = 0,	/* We have talked about suppress go ahead */
84 #endif	/* defined(TN3270) */
85 	telnetport,
86 	SYNCHing,	/* we are in TELNET SYNCH mode */
87 	flushout,	/* flush output */
88 	autoflush = 0,	/* flush output when interrupting? */
89 	autosynch,	/* send interrupt characters with SYNCH? */
90 	localflow,	/* we handle flow control locally */
91 	localchars,	/* we recognize interrupt/quit */
92 	donelclchars,	/* the user has set "localchars" */
93 	donebinarytoggle,	/* the user has put us in binary */
94 	dontlecho,	/* do we suppress local echoing right now? */
95 	globalmode;
96 
97 #define	CONTROL(x)	((x)&0x1f)		/* CTRL(x) is not portable */
98 
99 char
100 	*prompt = 0,
101 	escape,
102 	echoc;
103 
104 /*
105  * Telnet receiver states for fsm
106  */
107 #define	TS_DATA		0
108 #define	TS_IAC		1
109 #define	TS_WILL		2
110 #define	TS_WONT		3
111 #define	TS_DO		4
112 #define	TS_DONT		5
113 #define	TS_CR		6
114 #define	TS_SB		7		/* sub-option collection */
115 #define	TS_SE		8		/* looking for sub-option end */
116 
117 static int	telrcv_state;
118 
119 jmp_buf	toplevel = { 0 };
120 jmp_buf	peerdied;
121 
122 int	flushline;
123 
124 /*
125  * The following are some clocks used to decide how to interpret
126  * the relationship between various variables.
127  */
128 
129 Clocks clocks;
130 
131 Modelist modelist[] = {
132 	{ "telnet command mode", COMMAND_LINE },
133 	{ "character-at-a-time mode", 0 },
134 	{ "character-at-a-time mode (local echo)", LOCAL_ECHO|LOCAL_CHARS },
135 	{ "line-by-line mode (remote echo)", LINE | LOCAL_CHARS },
136 	{ "line-by-line mode", LINE | LOCAL_ECHO | LOCAL_CHARS },
137 	{ "line-by-line mode (local echoing suppressed)", LINE | LOCAL_CHARS },
138 	{ "3270 mode", 0 },
139 };
140 
141 
142 /*
143  * Initialize telnet environment.
144  */
145 
146 init_telnet()
147 {
148     SB_CLEAR();
149     ClearArray(options);
150 
151     connected = In3270 = ISend = localflow = donebinarytoggle = 0;
152 
153     SYNCHing = 0;
154 
155     /* Don't change NetTrace */
156 
157     escape = CONTROL(']');
158     echoc = CONTROL('E');
159 
160     flushline = 1;
161     telrcv_state = TS_DATA;
162 }
163 
164 
165 #include <varargs.h>
166 
167 /*VARARGS*/
168 static void
169 printring(va_alist)
170 va_dcl
171 {
172     va_list ap;
173     char buffer[100];		/* where things go */
174     char *ptr;
175     char *format;
176     char *string;
177     Ring *ring;
178     int i;
179 
180     va_start(ap);
181 
182     ring = va_arg(ap, Ring *);
183     format = va_arg(ap, char *);
184     ptr = buffer;
185 
186     while ((i = *format++) != 0) {
187 	if (i == '%') {
188 	    i = *format++;
189 	    switch (i) {
190 	    case 'c':
191 		*ptr++ = va_arg(ap, int);
192 		break;
193 	    case 's':
194 		string = va_arg(ap, char *);
195 		ring_supply_data(ring, buffer, ptr-buffer);
196 		ring_supply_data(ring, string, strlen(string));
197 		ptr = buffer;
198 		break;
199 	    case 0:
200 		ExitString("printring: trailing %%.\n", 1);
201 		/*NOTREACHED*/
202 	    default:
203 		ExitString("printring: unknown format character.\n", 1);
204 		/*NOTREACHED*/
205 	    }
206 	} else {
207 	    *ptr++ = i;
208 	}
209     }
210     ring_supply_data(ring, buffer, ptr-buffer);
211 }
212 
213 /*
214  * These routines are in charge of sending option negotiations
215  * to the other side.
216  *
217  * The basic idea is that we send the negotiation if either side
218  * is in disagreement as to what the current state should be.
219  */
220 
221 void
222 send_do(c)
223 {
224     if (!(did_he_say_will(c) && did_I_say_do(c))) {
225 	NET2ADD(IAC, DO);
226 	NETADD(c);
227 	I_said_do(c);
228 	printoption("SENT", "do", c);
229     }
230 }
231 
232 void
233 send_dont(c)
234 {
235     if (did_he_say_will(c) || did_I_say_do(c)) {
236 	NET2ADD(IAC, DONT);
237 	NETADD(c);
238 	I_said_dont(c);
239 	printoption("SENT", "dont", c);
240     }
241 }
242 
243 void
244 send_will(c)
245 {
246     if (!(did_he_say_do(c) && did_I_say_will(c))) {
247 	NET2ADD(IAC, WILL);
248 	NETADD(c);
249 	I_said_will(c);
250 	printoption("SENT", "will", c);
251     }
252 }
253 
254 void
255 send_wont(c)
256 {
257     if (did_he_say_do(c) || did_I_say_will(c)) {
258 	NET2ADD(IAC, WONT);
259 	NETADD(c);
260 	I_said_wont(c);
261 	printoption("SENT", "wont", c);
262     }
263 }
264 
265 
266 void
267 willoption(option)
268 	int option;
269 {
270 	char *fmt;
271 
272 	he_said_will(option);
273 
274 	switch (option) {
275 
276 	case TELOPT_ECHO:
277 #	if defined(TN3270)
278 	    /*
279 	     * The following is a pain in the rear-end.
280 	     * Various IBM servers (some versions of Wiscnet,
281 	     * possibly Fibronics/Spartacus, and who knows who
282 	     * else) will NOT allow us to send "DO SGA" too early
283 	     * in the setup proceedings.  On the other hand,
284 	     * 4.2 servers (telnetd) won't set SGA correctly.
285 	     * So, we are stuck.  Empirically (but, based on
286 	     * a VERY small sample), the IBM servers don't send
287 	     * out anything about ECHO, so we postpone our sending
288 	     * "DO SGA" until we see "WILL ECHO" (which 4.2 servers
289 	     * DO send).
290 	     */
291 	    {
292 		if (askedSGA == 0) {
293 		    askedSGA = 1;
294 		    if (!did_I_say_do(TELOPT_SGA)) {
295 			send_do(TELOPT_SGA);
296 		    }
297 		}
298 	    }
299 		/* Fall through */
300 	case TELOPT_EOR:
301 	case TELOPT_BINARY:
302 #endif	/* defined(TN3270) */
303 	case TELOPT_SGA:
304 		settimer(modenegotiated);
305 		send_do(option);
306 		setconnmode();		/* possibly set new tty mode */
307 		break;
308 
309 	case TELOPT_TM:
310 		return;			/* Never reply to TM will's/wont's */
311 
312 	default:
313 		send_dont(option);
314 		break;
315 	}
316 }
317 
318 void
319 wontoption(option)
320 	int option;
321 {
322 	char *fmt;
323 
324 	he_said_wont(option);
325 
326 	switch (option) {
327 
328 	case TELOPT_ECHO:
329 	case TELOPT_SGA:
330 		settimer(modenegotiated);
331 		send_dont(option);
332 		setconnmode();			/* Set new tty mode */
333 		break;
334 
335 	case TELOPT_TM:
336 		return;		/* Never reply to TM will's/wont's */
337 
338 	default:
339 		send_dont(option);
340 	}
341 }
342 
343 static void
344 dooption(option)
345 	int option;
346 {
347 	char *fmt;
348 
349 	he_said_do(option);
350 
351 	switch (option) {
352 
353 	case TELOPT_TM:
354 #	if defined(TN3270)
355 	case TELOPT_EOR:		/* end of record */
356 	case TELOPT_BINARY:		/* binary mode */
357 #	endif	/* defined(TN3270) */
358 	case TELOPT_NAWS:		/* window size */
359 	case TELOPT_TSPEED:		/* terminal speed */
360 	case TELOPT_LFLOW:		/* local flow control */
361 	case TELOPT_TTYPE:		/* terminal type option */
362 	case TELOPT_SGA:		/* no big deal */
363 		send_will(option);
364 		break;
365 
366 	case TELOPT_ECHO:		/* We're never going to echo... */
367 	default:
368 		send_wont(option);
369 		break;
370 	}
371 }
372 
373 /*
374  * suboption()
375  *
376  *	Look at the sub-option buffer, and try to be helpful to the other
377  * side.
378  *
379  *	Currently we recognize:
380  *
381  *		Terminal type, send request.
382  *		Terminal speed (send request).
383  *		Local flow control (is request).
384  */
385 
386 static void
387 suboption()
388 {
389     printsub("<", subbuffer, subend-subbuffer+1);
390     switch (subbuffer[0]&0xff) {
391     case TELOPT_TTYPE:
392 	if ((subbuffer[1]&0xff) != TELQUAL_SEND) {
393 	    ;
394 	} else {
395 	    char *name;
396 	    char namebuf[41];
397 	    extern char *getenv();
398 	    int len;
399 
400 #if	defined(TN3270)
401 	    if (tn3270_ttype()) {
402 		return;
403 	    }
404 #endif	/* defined(TN3270) */
405 	    name = getenv("TERM");
406 	    if ((name == 0) || ((len = strlen(name)) > 40)) {
407 		name = "UNKNOWN";
408 		len = strlen(name);
409 	    }
410 	    if ((len + 4+2) < NETROOM()) {
411 		char temp[50];
412 
413 		strcpy(namebuf, name);
414 		upcase(namebuf);
415 		sprintf(temp, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE,
416 				    TELQUAL_IS, namebuf, IAC, SE);
417 		ring_supply_data(&netoring, temp, 4+strlen(namebuf)+2);
418 		printsub(">", temp+2, 4+strlen(namebuf)+2-2-2);
419 	    } else {
420 		ExitString("No room in buffer for terminal type.\n", 1);
421 		/*NOTREACHED*/
422 	    }
423 	}
424 	break;
425     case TELOPT_TSPEED:
426 	if ((subbuffer[1]&0xff) == TELQUAL_SEND) {
427 	    long *ospeed,*ispeed;
428 	    char speedbuf[41];
429 	    char *getenv();
430 	    int len;
431 
432 	    TerminalSpeeds(&ispeed, &ospeed);
433 
434 	    sprintf(speedbuf, "%d,%d", ospeed, ispeed);
435 	    len = strlen(speedbuf);
436 
437 	    if ((len + 4+2) < NETROOM()) {
438 		printring(&netoring, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TSPEED,
439 		    TELQUAL_IS, speedbuf, IAC, SE);
440 	    }
441 	}
442 	break;
443     case TELOPT_LFLOW:
444 	if ((subbuffer[1]&0xff) == 1) {
445 	    localflow = 1;
446 	} else if ((subbuffer[1]&0xff) == 0) {
447 	    localflow = 0;
448 	}
449 	setcommandmode();
450 	setconnmode();
451 	break;
452     default:
453 	break;
454     }
455 }
456 
457 
458 int
459 telrcv()
460 {
461     register int c;
462     register int scc;
463     register char *sbp;
464     int count;
465     int returnValue = 0;
466 
467     scc = 0;
468     count = 0;
469     while (TTYROOM() > 2) {
470 	if (scc == 0) {
471 	    if (count) {
472 		ring_consumed(&netiring, count);
473 		returnValue = 1;
474 		count = 0;
475 	    }
476 	    sbp = netiring.consume;
477 	    scc = ring_full_consecutive(&netiring);
478 	    if (scc == 0) {
479 		/* No more data coming in */
480 		break;
481 	    }
482 	}
483 
484 	c = *sbp++ & 0xff, scc--; count++;
485 
486 	switch (telrcv_state) {
487 
488 	case TS_CR:
489 	    telrcv_state = TS_DATA;
490 	    if (c == '\0') {
491 		break;	/* Ignore \0 after CR */
492 	    } else if ((c == '\n') && (!should_he(TELOPT_ECHO)) && !crmod) {
493 		TTYADD(c);
494 		break;
495 	    }
496 	    /* Else, fall through */
497 
498 	case TS_DATA:
499 	    if (c == IAC) {
500 		telrcv_state = TS_IAC;
501 		break;
502 	    }
503 #	    if defined(TN3270)
504 	    if (In3270) {
505 		*Ifrontp++ = c;
506 		while (scc > 0) {
507 		    c = *sbp++ & 0377, scc--; count++;
508 		    if (c == IAC) {
509 			telrcv_state = TS_IAC;
510 			break;
511 		    }
512 		    *Ifrontp++ = c;
513 		}
514 	    } else
515 #	    endif /* defined(TN3270) */
516 		    /*
517 		     * The 'crmod' hack (see following) is needed
518 		     * since we can't * set CRMOD on output only.
519 		     * Machines like MULTICS like to send \r without
520 		     * \n; since we must turn off CRMOD to get proper
521 		     * input, the mapping is done here (sigh).
522 		     */
523 	    if ((c == '\r') && !should_he(TELOPT_BINARY)) {
524 		if (scc > 0) {
525 		    c = *sbp&0xff;
526 		    if (c == 0) {
527 			sbp++, scc--; count++;
528 			/* a "true" CR */
529 			TTYADD('\r');
530 		    } else if (!should_he(TELOPT_ECHO) &&
531 					(c == '\n')) {
532 			sbp++, scc--; count++;
533 			TTYADD('\n');
534 		    } else {
535 			TTYADD('\r');
536 			if (crmod) {
537 				TTYADD('\n');
538 			}
539 		    }
540 		} else {
541 		    telrcv_state = TS_CR;
542 		    TTYADD('\r');
543 		    if (crmod) {
544 			    TTYADD('\n');
545 		    }
546 		}
547 	    } else {
548 		TTYADD(c);
549 	    }
550 	    continue;
551 
552 	case TS_IAC:
553 	    switch (c) {
554 
555 	    case WILL:
556 		telrcv_state = TS_WILL;
557 		continue;
558 
559 	    case WONT:
560 		telrcv_state = TS_WONT;
561 		continue;
562 
563 	    case DO:
564 		telrcv_state = TS_DO;
565 		continue;
566 
567 	    case DONT:
568 		telrcv_state = TS_DONT;
569 		continue;
570 
571 	    case DM:
572 		    /*
573 		     * We may have missed an urgent notification,
574 		     * so make sure we flush whatever is in the
575 		     * buffer currently.
576 		     */
577 		SYNCHing = 1;
578 		ttyflush(1);
579 		SYNCHing = stilloob();
580 		settimer(gotDM);
581 		break;
582 
583 	    case NOP:
584 	    case GA:
585 		break;
586 
587 	    case SB:
588 		SB_CLEAR();
589 		telrcv_state = TS_SB;
590 		continue;
591 
592 #	    if defined(TN3270)
593 	    case EOR:
594 		if (In3270) {
595 		    Ibackp += DataFromNetwork(Ibackp, Ifrontp-Ibackp, 1);
596 		    if (Ibackp == Ifrontp) {
597 			Ibackp = Ifrontp = Ibuf;
598 			ISend = 0;	/* should have been! */
599 		    } else {
600 			ISend = 1;
601 		    }
602 		}
603 		break;
604 #	    endif /* defined(TN3270) */
605 
606 	    case IAC:
607 #	    if !defined(TN3270)
608 		TTYADD(IAC);
609 #	    else /* !defined(TN3270) */
610 		if (In3270) {
611 		    *Ifrontp++ = IAC;
612 		} else {
613 		    TTYADD(IAC);
614 		}
615 #	    endif /* !defined(TN3270) */
616 		break;
617 
618 	    default:
619 		break;
620 	    }
621 	    telrcv_state = TS_DATA;
622 	    continue;
623 
624 	case TS_WILL:
625 	    printoption("RCVD", "will", c);
626 	    if (c == TELOPT_TM) {
627 		if (flushout) {
628 		    flushout = 0;
629 		}
630 	    } else {
631 		willoption(c);
632 	    }
633 	    SetIn3270();
634 	    telrcv_state = TS_DATA;
635 	    continue;
636 
637 	case TS_WONT:
638 	    printoption("RCVD", "wont", c);
639 	    if (c == TELOPT_TM) {
640 		if (flushout) {
641 		    flushout = 0;
642 		}
643 	    } else {
644 		wontoption(c, 1);
645 	    }
646 	    SetIn3270();
647 	    telrcv_state = TS_DATA;
648 	    continue;
649 
650 	case TS_DO:
651 	    printoption("RCVD", "do", c);
652 	    dooption(c);
653 	    SetIn3270();
654 	    if (c == TELOPT_NAWS) {
655 		sendnaws();
656 	    } else if (c == TELOPT_LFLOW) {
657 		localflow = 1;
658 		setcommandmode();
659 		setconnmode();
660 	    }
661 	    telrcv_state = TS_DATA;
662 	    continue;
663 
664 	/*
665 	 * Now, I've never understood this.  Why do we
666 	 * need separate routines for will, wont, do,
667 	 * but not for dont?
668 	 */
669 	case TS_DONT:
670 	    printoption("RCVD", "dont", c);
671 	    he_said_dont(c);
672 	    send_wont(c);
673 	    flushline = 1;
674 	    setconnmode();	/* set new tty mode (maybe) */
675 	    SetIn3270();
676 	    telrcv_state = TS_DATA;
677 	    continue;
678 
679 	case TS_SB:
680 	    if (c == IAC) {
681 		telrcv_state = TS_SE;
682 	    } else {
683 		SB_ACCUM(c);
684 	    }
685 	    continue;
686 
687 	case TS_SE:
688 	    if (c != SE) {
689 		if (c != IAC) {
690 		    SB_ACCUM(IAC);
691 		}
692 		SB_ACCUM(c);
693 		telrcv_state = TS_SB;
694 	    } else {
695 		SB_TERM();
696 		suboption();	/* handle sub-option */
697 		SetIn3270();
698 		telrcv_state = TS_DATA;
699 	    }
700 	}
701     }
702     if (count)
703 	ring_consumed(&netiring, count);
704     return returnValue||count;
705 }
706 
707 static int
708 telsnd()
709 {
710     int tcc;
711     int count;
712     int returnValue = 0;
713     char *tbp;
714 
715     tcc = 0;
716     count = 0;
717     while (NETROOM() > 2) {
718 	register int sc;
719 	register int c;
720 
721 	if (tcc == 0) {
722 	    if (count) {
723 		ring_consumed(&ttyiring, count);
724 		returnValue = 1;
725 		count = 0;
726 	    }
727 	    tbp = ttyiring.consume;
728 	    tcc = ring_full_consecutive(&ttyiring);
729 	    if (tcc == 0) {
730 		break;
731 	    }
732 	}
733 	c = *tbp++ & 0xff, sc = strip(c), tcc--; count++;
734 	if (sc == escape) {
735 	    command(0);
736 	    tcc = 0;
737 	    flushline = 1;
738 	    break;
739 	} else if (MODE_LINE(globalmode) && (sc == echoc)) {
740 	    if (tcc > 0 && strip(*tbp) == echoc) {
741 		tcc--; tbp++; count++;
742 	    } else {
743 		dontlecho = !dontlecho;
744 		settimer(echotoggle);
745 		setconnmode();
746 		flushline = 1;
747 		break;
748 	    }
749 	}
750 	if (localchars) {
751 	    if (TerminalSpecialChars(sc) == 0) {
752 		break;
753 	    }
754 	}
755 	if (!should_I(TELOPT_BINARY)) {
756 	    switch (c) {
757 	    case '\n':
758 		    /*
759 		     * If we are in CRMOD mode (\r ==> \n)
760 		     * on our local machine, then probably
761 		     * a newline (unix) is CRLF (TELNET).
762 		     */
763 		if (MODE_LOCAL_CHARS(globalmode)) {
764 		    NETADD('\r');
765 		}
766 		NETADD('\n');
767 		flushline = 1;
768 		break;
769 	    case '\r':
770 		if (!crlf) {
771 		    NET2ADD('\r', '\0');
772 		} else {
773 		    NET2ADD('\r', '\n');
774 		}
775 		flushline = 1;
776 		break;
777 	    case IAC:
778 		NET2ADD(IAC, IAC);
779 		break;
780 	    default:
781 		NETADD(c);
782 		break;
783 	    }
784 	} else if (c == IAC) {
785 	    NET2ADD(IAC, IAC);
786 	} else {
787 	    NETADD(c);
788 	}
789     }
790     if (count)
791 	ring_consumed(&ttyiring, count);
792     return returnValue||count;		/* Non-zero if we did anything */
793 }
794 
795 /*
796  * Scheduler()
797  *
798  * Try to do something.
799  *
800  * If we do something useful, return 1; else return 0.
801  *
802  */
803 
804 
805 int
806 Scheduler(block)
807 int	block;			/* should we block in the select ? */
808 {
809 		/* One wants to be a bit careful about setting returnValue
810 		 * to one, since a one implies we did some useful work,
811 		 * and therefore probably won't be called to block next
812 		 * time (TN3270 mode only).
813 		 */
814     int returnValue;
815     int netin, netout, netex, ttyin, ttyout;
816 
817     /* Decide which rings should be processed */
818 
819     netout = ring_full_count(&netoring) &&
820 	    (!MODE_LINE(globalmode) || flushline || should_I(TELOPT_BINARY));
821     ttyout = ring_full_count(&ttyoring);
822 
823 #if	defined(TN3270)
824     ttyin = ring_empty_count(&ttyiring) && (shell_active == 0);
825 #else	/* defined(TN3270) */
826     ttyin = ring_empty_count(&ttyiring);
827 #endif	/* defined(TN3270) */
828 
829 #if	defined(TN3270)
830     netin = ring_empty_count(&netiring);
831 #   else /* !defined(TN3270) */
832     netin = !ISend && ring_empty_count(&netiring);
833 #   endif /* !defined(TN3270) */
834 
835     netex = !SYNCHing;
836 
837     /* If we have seen a signal recently, reset things */
838 #   if defined(TN3270) && defined(unix)
839     if (HaveInput) {
840 	HaveInput = 0;
841 	signal(SIGIO, inputAvailable);
842     }
843 #endif	/* defined(TN3270) && defined(unix) */
844 
845     /* Call to system code to process rings */
846 
847     returnValue = process_rings(netin, netout, netex, ttyin, ttyout, !block);
848 
849     /* Now, look at the input rings, looking for work to do. */
850 
851     if (ring_full_count(&ttyiring)) {
852 #   if defined(TN3270)
853 	if (In3270) {
854 	    int c;
855 
856 	    c = DataFromTerminal(ttyiring.consume,
857 					ring_full_consecutive(&ttyiring));
858 	    if (c) {
859 		returnValue = 1;
860 	        ring_consumed(&ttyiring, c);
861 	    }
862 	} else {
863 #   endif /* defined(TN3270) */
864 	    returnValue |= telsnd();
865 #   if defined(TN3270)
866 	}
867 #   endif /* defined(TN3270) */
868     }
869 
870     if (ring_full_count(&netiring)) {
871 #	if !defined(TN3270)
872 	returnValue |= telrcv();
873 #	else /* !defined(TN3270) */
874 	returnValue = Push3270();
875 #	endif /* !defined(TN3270) */
876     }
877     return returnValue;
878 }
879 
880 /*
881  * Select from tty and network...
882  */
883 void
884 telnet()
885 {
886     sys_telnet_init();
887 
888 #   if !defined(TN3270)
889     if (telnetport) {
890 	send_do(TELOPT_SGA);
891 	send_will(TELOPT_TTYPE);
892 	send_will(TELOPT_NAWS);
893 	send_will(TELOPT_TSPEED);
894 	send_will(TELOPT_LFLOW);
895     }
896 #   endif /* !defined(TN3270) */
897 
898 #   if !defined(TN3270)
899     for (;;) {
900 	int schedValue;
901 
902 	while ((schedValue = Scheduler(0)) != 0) {
903 	    if (schedValue == -1) {
904 		setcommandmode();
905 		return;
906 	    }
907 	}
908 
909 	if (Scheduler(1) == -1) {
910 	    setcommandmode();
911 	    return;
912 	}
913     }
914 #   else /* !defined(TN3270) */
915     for (;;) {
916 	int schedValue;
917 
918 	while (!In3270 && !shell_active) {
919 	    if (Scheduler(1) == -1) {
920 		setcommandmode();
921 		return;
922 	    }
923 	}
924 
925 	while ((schedValue = Scheduler(0)) != 0) {
926 	    if (schedValue == -1) {
927 		setcommandmode();
928 		return;
929 	    }
930 	}
931 		/* If there is data waiting to go out to terminal, don't
932 		 * schedule any more data for the terminal.
933 		 */
934 	if (ring_full_count(&ttyoring)) {
935 	    schedValue = 1;
936 	} else {
937 	    if (shell_active) {
938 		if (shell_continue() == 0) {
939 		    ConnectScreen();
940 		}
941 	    } else if (In3270) {
942 		schedValue = DoTerminalOutput();
943 	    }
944 	}
945 	if (schedValue && (shell_active == 0)) {
946 	    if (Scheduler(1) == -1) {
947 		setcommandmode();
948 		return;
949 	    }
950 	}
951     }
952 #   endif /* !defined(TN3270) */
953 }
954 
955 #if	0	/* XXX - this not being in is a bug */
956 /*
957  * nextitem()
958  *
959  *	Return the address of the next "item" in the TELNET data
960  * stream.  This will be the address of the next character if
961  * the current address is a user data character, or it will
962  * be the address of the character following the TELNET command
963  * if the current address is a TELNET IAC ("I Am a Command")
964  * character.
965  */
966 
967 static char *
968 nextitem(current)
969 char	*current;
970 {
971     if ((*current&0xff) != IAC) {
972 	return current+1;
973     }
974     switch (*(current+1)&0xff) {
975     case DO:
976     case DONT:
977     case WILL:
978     case WONT:
979 	return current+3;
980     case SB:		/* loop forever looking for the SE */
981 	{
982 	    register char *look = current+2;
983 
984 	    for (;;) {
985 		if ((*look++&0xff) == IAC) {
986 		    if ((*look++&0xff) == SE) {
987 			return look;
988 		    }
989 		}
990 	    }
991 	}
992     default:
993 	return current+2;
994     }
995 }
996 #endif	/* 0 */
997 
998 /*
999  * netclear()
1000  *
1001  *	We are about to do a TELNET SYNCH operation.  Clear
1002  * the path to the network.
1003  *
1004  *	Things are a bit tricky since we may have sent the first
1005  * byte or so of a previous TELNET command into the network.
1006  * So, we have to scan the network buffer from the beginning
1007  * until we are up to where we want to be.
1008  *
1009  *	A side effect of what we do, just to keep things
1010  * simple, is to clear the urgent data pointer.  The principal
1011  * caller should be setting the urgent data pointer AFTER calling
1012  * us in any case.
1013  */
1014 
1015 static void
1016 netclear()
1017 {
1018 #if	0	/* XXX */
1019     register char *thisitem, *next;
1020     char *good;
1021 #define	wewant(p)	((nfrontp > p) && ((*p&0xff) == IAC) && \
1022 				((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
1023 
1024     thisitem = netobuf;
1025 
1026     while ((next = nextitem(thisitem)) <= netobuf.send) {
1027 	thisitem = next;
1028     }
1029 
1030     /* Now, thisitem is first before/at boundary. */
1031 
1032     good = netobuf;	/* where the good bytes go */
1033 
1034     while (netoring.add > thisitem) {
1035 	if (wewant(thisitem)) {
1036 	    int length;
1037 
1038 	    next = thisitem;
1039 	    do {
1040 		next = nextitem(next);
1041 	    } while (wewant(next) && (nfrontp > next));
1042 	    length = next-thisitem;
1043 	    memcpy(good, thisitem, length);
1044 	    good += length;
1045 	    thisitem = next;
1046 	} else {
1047 	    thisitem = nextitem(thisitem);
1048 	}
1049     }
1050 
1051 #endif	/* 0 */
1052 }
1053 
1054 /*
1055  * These routines add various telnet commands to the data stream.
1056  */
1057 
1058 static void
1059 doflush()
1060 {
1061     NET2ADD(IAC, DO);
1062     NETADD(TELOPT_TM);
1063     flushline = 1;
1064     flushout = 1;
1065     ttyflush(1);			/* Flush/drop output */
1066     /* do printoption AFTER flush, otherwise the output gets tossed... */
1067     printoption("SENT", "do", TELOPT_TM);
1068 }
1069 
1070 void
1071 xmitAO()
1072 {
1073     NET2ADD(IAC, AO);
1074     if (autoflush) {
1075 	doflush();
1076     }
1077 }
1078 
1079 
1080 void
1081 xmitEL()
1082 {
1083     NET2ADD(IAC, EL);
1084 }
1085 
1086 void
1087 xmitEC()
1088 {
1089     NET2ADD(IAC, EC);
1090 }
1091 
1092 
1093 #if	defined(NOT43)
1094 int
1095 #else	/* defined(NOT43) */
1096 void
1097 #endif	/* defined(NOT43) */
1098 dosynch()
1099 {
1100     netclear();			/* clear the path to the network */
1101     NETADD(IAC);
1102     setneturg();
1103     NETADD(DM);
1104 
1105 #if	defined(NOT43)
1106     return 0;
1107 #endif	/* defined(NOT43) */
1108 }
1109 
1110 void
1111 intp()
1112 {
1113     NET2ADD(IAC, IP);
1114     flushline = 1;
1115     if (autoflush) {
1116 	doflush();
1117     }
1118     if (autosynch) {
1119 	dosynch();
1120     }
1121 }
1122 
1123 void
1124 sendbrk()
1125 {
1126     NET2ADD(IAC, BREAK);
1127     flushline = 1;
1128     if (autoflush) {
1129 	doflush();
1130     }
1131     if (autosynch) {
1132 	dosynch();
1133     }
1134 }
1135 /*
1136  * Send a window size update to the remote system.
1137  */
1138 
1139 void
1140 sendnaws()
1141 {
1142     long rows, cols;
1143 
1144 #define        NETADDCHAR(x) \
1145     { \
1146 	if (((x) & 0xff) == 0xff) { \
1147 	    NET2ADD(IAC, IAC) \
1148 	} else { \
1149 	    NETADD(x) \
1150 	} \
1151     }
1152 #define        NETADDSHORT(x)  { NETADDCHAR(x >> 8); NETADDCHAR(x & 0xff) }
1153 
1154     if (TerminalWindowSize(&rows, &cols) == 0) {	/* Failed */
1155 	return;
1156     }
1157 
1158     if (NETROOM() >= 3 + 8 + 2) {
1159 	NET2ADD(IAC, SB);
1160 	NETADD(TELOPT_NAWS);
1161 	NETADDSHORT(cols);
1162 	NETADDSHORT(rows);
1163 	NET2ADD(IAC, SE);
1164     }
1165 }
1166 
1167 tel_enter_binary()
1168 {
1169     send_do(TELOPT_BINARY);
1170     send_will(TELOPT_BINARY);
1171 }
1172 
1173 tel_leave_binary()
1174 {
1175     send_dont(TELOPT_BINARY);
1176     send_wont(TELOPT_BINARY);
1177 }
1178