xref: /csrg-svn/libexec/telnetd/telnetd.c (revision 39531)
1 /*
2  * Copyright (c) 1989 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 char copyright[] =
20 "@(#) Copyright (c) 1989 Regents of the University of California.\n\
21  All rights reserved.\n";
22 #endif /* not lint */
23 
24 #ifndef lint
25 static char sccsid[] = "@(#)telnetd.c	5.41 (Berkeley) 11/14/89";
26 #endif /* not lint */
27 
28 #include "telnetd.h"
29 
30 /*
31  * I/O data buffers,
32  * pointers, and counters.
33  */
34 char	ptyibuf[BUFSIZ], *ptyip = ptyibuf;
35 char	ptyibuf2[BUFSIZ];
36 
37 #ifdef	CRAY
38 int	hostinfo = 1;			/* do we print login banner? */
39 #endif
40 
41 #ifdef	CRAY
42 extern int      newmap; /* nonzero if \n maps to ^M^J */
43 int	lowpty = 0, highpty = 128;	/* low, high pty numbers */
44 #endif /* CRAY */
45 
46 int debug = 0;
47 char *progname;
48 
49 main(argc, argv)
50 	char *argv[];
51 {
52 	struct sockaddr_in from;
53 	int on = 1, fromlen;
54 
55 	pfrontp = pbackp = ptyobuf;
56 	netip = netibuf;
57 	nfrontp = nbackp = netobuf;
58 
59 	progname = *argv;
60 top:
61 	argc--, argv++;
62 
63 	if (argc > 0 && strcmp(*argv, "-debug") == 0) {
64 		debug++;
65 		goto top;
66 	}
67 
68 #ifdef	LINEMODE
69 	if (argc > 0 && !strcmp(*argv, "-l")) {
70 		alwayslinemode = 1;
71 		goto top;
72 	}
73 #endif	/* LINEMODE */
74 
75 #ifdef CRAY
76 	if (argc > 0 && !strcmp(*argv, "-h")) {
77 		hostinfo = 0;
78 		goto top;
79 	}
80 
81 	if (argc > 0 && !strncmp(*argv, "-r", 2)) {
82 		char *strchr();
83 		char *c;
84 
85 		*argv += 2;
86 		if (**argv == '\0' && argc)
87 			argv++, argc--;
88 		c = strchr(*argv, '-');
89 		if (c) {
90 			*c++ = '\0';
91 			highpty = atoi(c);
92 		} else
93 			highpty = -1;
94 		lowpty = atoi(*argv);
95 		if ((lowpty > highpty) || (lowpty < 0) || (highpty > 999)) {
96 	usage:
97 			fprintf(stderr, "Usage: telnetd [-debug] [-h] ");
98 # ifdef	NEWINIT
99 			fprintf(stderr, "[-Iinitid] ");
100 # endif	/* NEWINIT */
101 			fprintf(stderr, "[-l] [-rlowpty-highpty] [port]\n");
102 			exit(1);
103 		}
104 		goto top;
105 	}
106 # ifdef	NEWINIT
107 	if (argc > 0 && !strncmp(*argv, "-I", 2)) {
108 		extern char *gen_id;
109 
110 		*argv += 2;
111 		if (**argv == '\0') {
112 			if (argc < 2)
113 				goto usage;
114 			argv++, argc--;
115 			if (**argv == '\0')
116 				goto usage;
117 		}
118 		gen_id = *argv;
119 		goto top;
120 	}
121 # endif	/* NEWINIT */
122 #endif	/* CRAY */
123 
124 	if (debug) {
125 	    int s, ns, foo;
126 	    struct servent *sp;
127 	    static struct sockaddr_in sin = { AF_INET };
128 
129 	    if (argc > 0) {
130 		    if (sp = getservbyname(*argv, "tcp")) {
131 			sin.sin_port = sp->s_port;
132 		    } else {
133 			sin.sin_port = atoi(*argv);
134 			if ((int)sin.sin_port <= 0) {
135 			    fprintf(stderr, "telnetd: %s: bad port #\n", *argv);
136 			    exit(1);
137 			}
138 			sin.sin_port = htons((u_short)sin.sin_port);
139 		   }
140 	    } else {
141 		sp = getservbyname("telnet", "tcp");
142 		if (sp == 0) {
143 			fprintf(stderr,
144 				"telnetd: tcp/telnet: unknown service\n");
145 		    exit(1);
146 		}
147 		sin.sin_port = sp->s_port;
148 	    }
149 
150 	    s = socket(AF_INET, SOCK_STREAM, 0);
151 	    if (s < 0) {
152 		    perror("telnetd: socket");;
153 		    exit(1);
154 	    }
155 	    (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
156 	    if (bind(s, (struct sockaddr *)&sin, sizeof sin) < 0) {
157 		perror("bind");
158 		exit(1);
159 	    }
160 	    if (listen(s, 1) < 0) {
161 		perror("listen");
162 		exit(1);
163 	    }
164 	    foo = sizeof sin;
165 	    ns = accept(s, (struct sockaddr *)&sin, &foo);
166 	    if (ns < 0) {
167 		perror("accept");
168 		exit(1);
169 	    }
170 	    (void) dup2(ns, 0);
171 	    (void) close(ns);
172 	    (void) close(s);
173 	}
174 
175 	openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON);
176 	fromlen = sizeof (from);
177 	if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) {
178 		fprintf(stderr, "%s: ", progname);
179 		perror("getpeername");
180 		_exit(1);
181 	}
182 	if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) {
183 		syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
184 	}
185 	net = 0;
186 	doit(&from);
187 	/* NOTREACHED */
188 }  /* end of main */
189 
190 int	cleanup();
191 
192 /*
193  * getterminaltype
194  *
195  *	Ask the other end to send along its terminal type and speed.
196  * Output is the variable terminaltype filled in.
197  */
198 static char ttytype_sbbuf[] = { IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE };
199 void
200 getterminaltype()
201 {
202     void ttloop();
203 
204     settimer(baseline);
205     send_do(TELOPT_TTYPE, 1);
206     send_do(TELOPT_TSPEED, 1);
207     while ((hiswants[TELOPT_TTYPE] != hisopts[TELOPT_TTYPE]) ||
208 	   (hiswants[TELOPT_TSPEED] != hisopts[TELOPT_TSPEED])) {
209 	ttloop();
210     }
211     if (hisopts[TELOPT_TSPEED] == OPT_YES) {
212 	static char sbbuf[] = { IAC, SB, TELOPT_TSPEED, TELQUAL_SEND, IAC, SE };
213 
214 	bcopy(sbbuf, nfrontp, sizeof sbbuf);
215 	nfrontp += sizeof sbbuf;
216     }
217     if (hisopts[TELOPT_TTYPE] == OPT_YES) {
218 
219 	bcopy(ttytype_sbbuf, nfrontp, sizeof ttytype_sbbuf);
220 	nfrontp += sizeof ttytype_sbbuf;
221     }
222     if (hisopts[TELOPT_TSPEED] == OPT_YES) {
223 	while (sequenceIs(tspeedsubopt, baseline))
224 	    ttloop();
225     }
226     if (hisopts[TELOPT_TTYPE] == OPT_YES) {
227 	char first[256], last[256];
228 
229 	while (sequenceIs(ttypesubopt, baseline))
230 	    ttloop();
231 
232 	if (!terminaltypeok(&terminaltype[5])) {
233 	    (void) strncpy(first, terminaltype, sizeof(first));
234 	    for(;;) {
235 		/*
236 		 * Save the unknown name, and request the next name.
237 		 */
238 		(void) strncpy(last, terminaltype, sizeof(last));
239 		_gettermname();
240 		if (terminaltypeok(&terminaltype[5]))
241 		    break;
242 		if (strncmp(last, terminaltype, sizeof(last)) == 0) {
243 		    /*
244 		     * We've hit the end.  If this is the same as
245 		     * the first name, just go with it.
246 		     */
247 		    if (strncmp(first, terminaltype, sizeof(first) == 0))
248 			break;
249 		    /*
250 		     * Get the terminal name one more type, so that
251 		     * RFC1091 compliant telnets will cycle back to
252 		     * the start of the list.
253 		     */
254 		    _gettermname();
255 		    if (strncmp(first, terminaltype, sizeof(first) != 0))
256 			(void) strncpy(terminaltype, first, sizeof(first));
257 		    break;
258 		}
259 	    }
260 	}
261     }
262 }  /* end of getterminaltype */
263 
264 _gettermname()
265 {
266     settimer(baseline);
267     bcopy(ttytype_sbbuf, nfrontp, sizeof ttytype_sbbuf);
268     nfrontp += sizeof ttytype_sbbuf;
269     while (sequenceIs(ttypesubopt, baseline))
270 	ttloop();
271 }
272 
273 terminaltypeok(s)
274 char *s;
275 {
276     char buf[1024];
277 
278     if (terminaltype == NULL)
279 	return(1);
280 
281     /*
282      * tgetent() will return 1 if the type is known, and
283      * 0 if it is not known.  If it returns -1, it couldn't
284      * open the database.  But if we can't open the database,
285      * it won't help to say we failed, because we won't be
286      * able to verify anything else.  So, we treat -1 like 1.
287      */
288     if (tgetent(buf, s) == 0)
289 	return(0);
290     return(1);
291 }
292 
293 /*
294  * Get a pty, scan input lines.
295  */
296 doit(who)
297 	struct sockaddr_in *who;
298 {
299 	char *host, *inet_ntoa();
300 	int t;
301 	struct hostent *hp;
302 
303 	/*
304 	 * Find an available pty to use.
305 	 */
306 	pty = getpty();
307 	if (pty < 0)
308 		fatal(net, "All network ports in use");
309 
310 	t = getptyslave();
311 
312 	/* get name of connected client */
313 	hp = gethostbyaddr((char *)&who->sin_addr, sizeof (struct in_addr),
314 		who->sin_family);
315 	if (hp)
316 		host = hp->h_name;
317 	else
318 		host = inet_ntoa(who->sin_addr);
319 
320 	/*
321 	 * get terminal type.
322 	 */
323 	getterminaltype();
324 	if (terminaltype == NULL)
325 		terminaltype = "TERM=network";
326 
327 	/*
328 	 * Start up the login process on the slave side of the terminal
329 	 */
330 	startslave(t, host);
331 
332 	telnet(net, pty);  /* begin server processing */
333 	/*NOTREACHED*/
334 }  /* end of doit */
335 
336 #ifndef	MAXHOSTNAMELEN
337 #define	MAXHOSTNAMELEN 64
338 #endif	MAXHOSTNAMELEN
339 /*
340  * Main loop.  Select from pty and network, and
341  * hand data to telnet receiver finite state machine.
342  */
343 telnet(f, p)
344 int f, p;
345 {
346 	int on = 1;
347 	char hostname[MAXHOSTNAMELEN];
348 #ifdef	CRAY2
349 	int termstat();
350 	int interrupt(), sendbrk();
351 #endif
352 #define	TABBUFSIZ	512
353 	char	defent[TABBUFSIZ];
354 	char	defstrs[TABBUFSIZ];
355 #undef	TABBUFSIZ
356 	char *HE;
357 	char *HN;
358 	char *IM;
359 	void netflush();
360 
361 	/*
362 	 * Initialize the slc mapping table.
363 	 */
364 	get_slc_defaults();
365 
366 	/*
367 	 * Do some tests where it is desireable to wait for a response.
368 	 * Rather than doing them slowly, one at a time, do them all
369 	 * at once.
370 	 */
371 	if (!myopts[TELOPT_SGA])
372 		send_will(TELOPT_SGA, 1);
373 	/*
374 	 * Is the client side a 4.2 (NOT 4.3) system?  We need to know this
375 	 * because 4.2 clients are unable to deal with TCP urgent data.
376 	 *
377 	 * To find out, we send out a "DO ECHO".  If the remote system
378 	 * answers "WILL ECHO" it is probably a 4.2 client, and we note
379 	 * that fact ("WILL ECHO" ==> that the client will echo what
380 	 * WE, the server, sends it; it does NOT mean that the client will
381 	 * echo the terminal input).
382 	 */
383 	send_do(TELOPT_ECHO, 1);
384 
385 #ifdef	LINEMODE
386 	if (hisopts[TELOPT_LINEMODE] == OPT_NO) {
387 		/* Query the peer for linemode support by trying to negotiate
388 		 * the linemode option.
389 		 */
390 		linemode = 1;
391 		editmode = 0;
392 		send_do(TELOPT_LINEMODE, 1);  /* send do linemode */
393 	}
394 #endif	/* LINEMODE */
395 
396 	/*
397 	 * Send along a couple of other options that we wish to negotiate.
398 	 */
399 	send_do(TELOPT_NAWS, 1);
400 	send_will(TELOPT_STATUS, 1);
401 	flowmode = 1;  /* default flow control state */
402 	send_do(TELOPT_LFLOW, 1);
403 
404 	/*
405 	 * Spin, waiting for a response from the DO ECHO.  However,
406 	 * some REALLY DUMB telnets out there might not respond
407 	 * to the DO ECHO.  So, we spin looking for NAWS, (most dumb
408 	 * telnets so far seem to respond with WONT for a DO that
409 	 * they don't understand...) because by the time we get the
410 	 * response, it will already have processed the DO ECHO.
411 	 * Kludge upon kludge.
412 	 */
413 	while (hiswants[TELOPT_NAWS] != hisopts[TELOPT_NAWS])
414 		ttloop();
415 
416 	/*
417 	 * On the off chance that the telnet client is broken and does not
418 	 * respond to the DO ECHO we sent, (after all, we did send the
419 	 * DO NAWS negotiation after the DO ECHO, and we won't get here
420 	 * until a response to the DO NAWS comes back) simulate the
421 	 * receipt of a will echo.  This will also send a WONT ECHO
422 	 * to the client, since we assume that the client failed to
423 	 * respond because it believes that it is already in DO ECHO
424 	 * mode, which we do not want.
425 	 */
426 	if (hiswants[TELOPT_ECHO] == OPT_YES)
427 		willoption(TELOPT_ECHO);
428 
429 	/*
430 	 * Finally, to clean things up, we turn on our echo.  This
431 	 * will break stupid 4.2 telnets out of local terminal echo.
432 	 */
433 
434 	if (!myopts[TELOPT_ECHO])
435 		send_will(TELOPT_ECHO, 1);
436 
437 	/*
438 	 * Turn on packet mode, and default to line at at time mode.
439 	 */
440 	(void) ioctl(p, TIOCPKT, (char *)&on);
441 #ifdef	LINEMODE
442 	tty_setlinemode(1);
443 
444 # ifdef	KLUDGELINEMODE
445 	/*
446 	 * Continuing line mode support.  If client does not support
447 	 * real linemode, attempt to negotiate kludge linemode by sending
448 	 * the do timing mark sequence.
449 	 */
450 	if (lmodetype < REAL_LINEMODE)
451 		send_do(TELOPT_TM, 1);
452 # endif	/* KLUDGELINEMODE */
453 #endif	/* LINEMODE */
454 
455 	/*
456 	 * Call telrcv() once to pick up anything received during
457 	 * terminal type negotiation, 4.2/4.3 determination, and
458 	 * linemode negotiation.
459 	 */
460 	telrcv();
461 
462 	(void) ioctl(f, FIONBIO, (char *)&on);
463 	(void) ioctl(p, FIONBIO, (char *)&on);
464 #ifdef	CRAY2
465 	init_termdriver(f, p, interrupt, sendbrk);
466 #endif
467 
468 #if	defined(SO_OOBINLINE)
469 	(void) setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on);
470 #endif	/* defined(SO_OOBINLINE) */
471 
472 #ifdef	SIGTSTP
473 	(void) signal(SIGTSTP, SIG_IGN);
474 #endif
475 #ifdef	SIGTTOU
476 	/*
477 	 * Ignoring SIGTTOU keeps the kernel from blocking us
478 	 * in ttioct() in /sys/tty.c.
479 	 */
480 	(void) signal(SIGTTOU, SIG_IGN);
481 #endif
482 
483 	(void) signal(SIGCHLD, cleanup);
484 
485 #if	defined(CRAY2)
486 	/*
487 	 * Cray-2 will send a signal when pty modes are changed by slave
488 	 * side.  Set up signal handler now.
489 	 */
490 	if ((int)signal(SIGUSR1, termstat) < 0)
491 		perror("signal");
492 	else if (ioctl(p, TCSIGME, (char *)SIGUSR1) < 0)
493 		perror("ioctl:TCSIGME");
494 	/*
495 	 * Make processing loop check terminal characteristics early on.
496 	 */
497 	termstat();
498 #endif
499 
500 	(void) setpgrp(0, 0);
501 
502 	/*
503 	 * Show banner that getty never gave.
504 	 *
505 	 * We put the banner in the pty input buffer.  This way, it
506 	 * gets carriage return null processing, etc., just like all
507 	 * other pty --> client data.
508 	 */
509 
510 	(void) gethostname(hostname, sizeof (hostname));
511 
512 	if (getent(defent, "default") == 1) {
513 		char *getstr();
514 		char *cp=defstrs;
515 
516 		HE = getstr("he", &cp);
517 		HN = getstr("hn", &cp);
518 		IM = getstr("im", &cp);
519 		if (HN && *HN)
520 			(void) strcpy(hostname, HN);
521 		if (IM == 0)
522 			IM = "";
523 	} else {
524 #ifdef	CRAY
525 		if (hostinfo == 0)
526 			IM = 0;
527 		else
528 #endif
529 			IM = DEFAULT_IM;
530 		HE = 0;
531 	}
532 	edithost(HE, hostname);
533 	if (IM && *IM)
534 		putf(IM, ptyibuf2);
535 
536 	if (pcc)
537 		(void) strncat(ptyibuf2, ptyip, pcc+1);
538 	ptyip = ptyibuf2;
539 	pcc = strlen(ptyip);
540 
541 	for (;;) {
542 		fd_set ibits, obits, xbits;
543 		register int c;
544 
545 		if (ncc < 0 && pcc < 0)
546 			break;
547 
548 #ifdef	CRAY2
549 		if (needtermstat)
550 			_termstat();
551 #endif	/* CRAY2 */
552 		FD_ZERO(&ibits);
553 		FD_ZERO(&obits);
554 		FD_ZERO(&xbits);
555 		/*
556 		 * Never look for input if there's still
557 		 * stuff in the corresponding output buffer
558 		 */
559 		if (nfrontp - nbackp || pcc > 0) {
560 			FD_SET(f, &obits);
561 		} else {
562 			FD_SET(p, &ibits);
563 		}
564 		if (pfrontp - pbackp || ncc > 0) {
565 			FD_SET(p, &obits);
566 		} else {
567 			FD_SET(f, &ibits);
568 		}
569 		if (!SYNCHing) {
570 			FD_SET(f, &xbits);
571 		}
572 		if ((c = select(16, &ibits, &obits, &xbits,
573 						(struct timeval *)0)) < 1) {
574 			if (c == -1) {
575 				if (errno == EINTR) {
576 					continue;
577 				}
578 			}
579 			sleep(5);
580 			continue;
581 		}
582 
583 		/*
584 		 * Any urgent data?
585 		 */
586 		if (FD_ISSET(net, &xbits)) {
587 		    SYNCHing = 1;
588 		}
589 
590 		/*
591 		 * Something to read from the network...
592 		 */
593 		if (FD_ISSET(net, &ibits)) {
594 #if	!defined(SO_OOBINLINE)
595 			/*
596 			 * In 4.2 (and 4.3 beta) systems, the
597 			 * OOB indication and data handling in the kernel
598 			 * is such that if two separate TCP Urgent requests
599 			 * come in, one byte of TCP data will be overlaid.
600 			 * This is fatal for Telnet, but we try to live
601 			 * with it.
602 			 *
603 			 * In addition, in 4.2 (and...), a special protocol
604 			 * is needed to pick up the TCP Urgent data in
605 			 * the correct sequence.
606 			 *
607 			 * What we do is:  if we think we are in urgent
608 			 * mode, we look to see if we are "at the mark".
609 			 * If we are, we do an OOB receive.  If we run
610 			 * this twice, we will do the OOB receive twice,
611 			 * but the second will fail, since the second
612 			 * time we were "at the mark", but there wasn't
613 			 * any data there (the kernel doesn't reset
614 			 * "at the mark" until we do a normal read).
615 			 * Once we've read the OOB data, we go ahead
616 			 * and do normal reads.
617 			 *
618 			 * There is also another problem, which is that
619 			 * since the OOB byte we read doesn't put us
620 			 * out of OOB state, and since that byte is most
621 			 * likely the TELNET DM (data mark), we would
622 			 * stay in the TELNET SYNCH (SYNCHing) state.
623 			 * So, clocks to the rescue.  If we've "just"
624 			 * received a DM, then we test for the
625 			 * presence of OOB data when the receive OOB
626 			 * fails (and AFTER we did the normal mode read
627 			 * to clear "at the mark").
628 			 */
629 		    if (SYNCHing) {
630 			int atmark;
631 
632 			(void) ioctl(net, SIOCATMARK, (char *)&atmark);
633 			if (atmark) {
634 			    ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB);
635 			    if ((ncc == -1) && (errno == EINVAL)) {
636 				ncc = read(net, netibuf, sizeof (netibuf));
637 				if (sequenceIs(didnetreceive, gotDM)) {
638 				    SYNCHing = stilloob(net);
639 				}
640 			    }
641 			} else {
642 			    ncc = read(net, netibuf, sizeof (netibuf));
643 			}
644 		    } else {
645 			ncc = read(net, netibuf, sizeof (netibuf));
646 		    }
647 		    settimer(didnetreceive);
648 #else	/* !defined(SO_OOBINLINE)) */
649 		    ncc = read(net, netibuf, sizeof (netibuf));
650 #endif	/* !defined(SO_OOBINLINE)) */
651 		    if (ncc < 0 && errno == EWOULDBLOCK)
652 			ncc = 0;
653 		    else {
654 			if (ncc <= 0) {
655 			    break;
656 			}
657 			netip = netibuf;
658 		    }
659 		}
660 
661 		/*
662 		 * Something to read from the pty...
663 		 */
664 		if (FD_ISSET(p, &ibits)) {
665 			pcc = read(p, ptyibuf, BUFSIZ);
666 			if (pcc < 0 && errno == EWOULDBLOCK)
667 				pcc = 0;
668 			else {
669 				if (pcc <= 0)
670 					break;
671 #ifndef	CRAY2
672 #ifdef	LINEMODE
673 				/*
674 				 * If ioctl from pty, pass it through net
675 				 */
676 				if (ptyibuf[0] & TIOCPKT_IOCTL) {
677 					copy_termbuf(ptyibuf+1, pcc-1);
678 					localstat();
679 					pcc = 1;
680 				}
681 #endif	LINEMODE
682 				if (ptyibuf[0] & TIOCPKT_FLUSHWRITE) {
683 					netclear();	/* clear buffer back */
684 					*nfrontp++ = IAC;
685 					*nfrontp++ = DM;
686 					neturg = nfrontp-1; /* off by one XXX */
687 				}
688 				if (hisopts[TELOPT_LFLOW] &&
689 				    (ptyibuf[0] &
690 				     (TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))) {
691 					(void) sprintf(nfrontp, "%c%c%c%c%c%c",
692 					    IAC, SB, TELOPT_LFLOW,
693 					    ptyibuf[0] & TIOCPKT_DOSTOP ? 1 : 0,
694 					    IAC, SE);
695 					nfrontp += 6;
696 				}
697 				pcc--;
698 				ptyip = ptyibuf+1;
699 #else	/* CRAY2 */
700 				if (!uselinemode) {
701 					unpcc = pcc;
702 					unptyip = ptyibuf;
703 					pcc = term_output(&unptyip, ptyibuf2,
704 								&unpcc, BUFSIZ);
705 					ptyip = ptyibuf2;
706 				} else
707 					ptyip = ptyibuf;
708 #endif	/* CRAY2 */
709 			}
710 		}
711 
712 		while (pcc > 0) {
713 			if ((&netobuf[BUFSIZ] - nfrontp) < 2)
714 				break;
715 			c = *ptyip++ & 0377, pcc--;
716 			if (c == IAC)
717 				*nfrontp++ = c;
718 #ifdef	CRAY2
719 			else if (c == '\n' &&
720 				     myopts[TELOPT_BINARY] == OPT_NO && newmap)
721 				*nfrontp++ = '\r';
722 #endif	/* CRAY2 */
723 			*nfrontp++ = c;
724 			if ((c == '\r') && (myopts[TELOPT_BINARY] == OPT_NO)) {
725 				if (pcc > 0 && ((*ptyip & 0377) == '\n')) {
726 					*nfrontp++ = *ptyip++ & 0377;
727 					pcc--;
728 				} else
729 					*nfrontp++ = '\0';
730 			}
731 		}
732 #ifdef CRAY2
733 		/*
734 		 * If chars were left over from the terminal driver,
735 		 * note their existence.
736 		 */
737 		 if (!uselinemode && unpcc) {
738 			pcc = unpcc;
739 			unpcc = 0;
740 			ptyip = unptyip;
741 		}
742 #endif /* CRAY2 */
743 
744 		if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0)
745 			netflush();
746 		if (ncc > 0)
747 			telrcv();
748 		if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0)
749 			ptyflush();
750 	}
751 	cleanup();
752 }  /* end of telnet */
753 
754 #ifndef	TCSIG
755 # ifdef	TIOCSIG
756 #  define TCSIG TIOCSIG
757 # endif
758 #endif
759 
760 /*
761  * Send interrupt to process on other side of pty.
762  * If it is in raw mode, just write NULL;
763  * otherwise, write intr char.
764  */
765 interrupt()
766 {
767 	ptyflush();	/* half-hearted */
768 
769 #ifdef	TCSIG
770 	(void) ioctl(pty, TCSIG, (char *)SIGINT);
771 #else	/* TCSIG */
772 	init_termbuf();
773 	*pfrontp++ = slctab[SLC_IP].sptr ? *slctab[SLC_IP].sptr : '\177';
774 #endif	/* TCSIG */
775 }
776 
777 /*
778  * Send quit to process on other side of pty.
779  * If it is in raw mode, just write NULL;
780  * otherwise, write quit char.
781  */
782 sendbrk()
783 {
784 	ptyflush();	/* half-hearted */
785 #ifdef	TCSIG
786 	(void) ioctl(pty, TCSIG, (char *)SIGQUIT);
787 #else	/* TCSIG */
788 	init_termbuf();
789 	*pfrontp++ = slctab[SLC_ABORT].sptr ? *slctab[SLC_ABORT].sptr : '\034';
790 #endif	/* TCSIG */
791 }
792 
793 sendsusp()
794 {
795 #ifdef	SIGTSTP
796 	ptyflush();	/* half-hearted */
797 # ifdef	TCSIG
798 	(void) ioctl(pty, TCSIG, (char *)SIGTSTP);
799 # else	/* TCSIG */
800 	*pfrontp++ = slctab[SLC_SUSP].sptr ? *slctab[SLC_SUSP].sptr : '\032';
801 # endif	/* TCSIG */
802 #endif	/* SIGTSTP */
803 }
804 
805 doeof()
806 {
807 	init_termbuf();
808 
809 	*pfrontp++ = slctab[SLC_EOF].sptr ? *slctab[SLC_EOF].sptr : '\004';
810 }
811