Change negotiation of ECHO for dumb 4.2 telnets
[unix-history] / usr / src / libexec / telnetd / telnetd.c
CommitLineData
8c5eec2f 1/*
ea139302 2 * Copyright (c) 1989 Regents of the University of California.
897ce52e
KB
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms are permitted
b8c620d6
KB
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.
8c5eec2f
DF
16 */
17
18#ifndef lint
19char copyright[] =
ea139302 20"@(#) Copyright (c) 1989 Regents of the University of California.\n\
8c5eec2f 21 All rights reserved.\n";
897ce52e 22#endif /* not lint */
8c5eec2f 23
ac6e6727 24#ifndef lint
86b6dfea 25static char sccsid[] = "@(#)telnetd.c 5.39 (Berkeley) %G%";
897ce52e 26#endif /* not lint */
ac6e6727 27
ea139302 28#include "telnetd.h"
66b878f6
BJ
29
30/*
ea139302
PB
31 * I/O data buffers,
32 * pointers, and counters.
66b878f6
BJ
33 */
34char ptyibuf[BUFSIZ], *ptyip = ptyibuf;
ea139302 35char ptyibuf2[BUFSIZ];
affdaa4e 36
ea139302
PB
37#ifdef CRAY
38int hostinfo = 1; /* do we print login banner? */
39#endif
affdaa4e 40
ea139302
PB
41#ifdef CRAY
42extern int newmap; /* nonzero if \n maps to ^M^J */
43int lowpty = 0, highpty = 128; /* low, high pty numbers */
44#endif /* CRAY */
affdaa4e 45
ea139302
PB
46int debug = 0;
47char *progname;
66b878f6 48
66b878f6
BJ
49main(argc, argv)
50 char *argv[];
51{
bb933cc2 52 struct sockaddr_in from;
bcb894cb 53 int on = 1, fromlen;
bb933cc2 54
ea139302
PB
55 pfrontp = pbackp = ptyobuf;
56 netip = netibuf;
57 nfrontp = nbackp = netobuf;
58
59 progname = *argv;
60top:
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) {
5d78ef73
GM
125 int s, ns, foo;
126 struct servent *sp;
127 static struct sockaddr_in sin = { AF_INET };
128
ea139302
PB
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 }
31004941
GM
140 } else {
141 sp = getservbyname("telnet", "tcp");
142 if (sp == 0) {
143 fprintf(stderr,
144 "telnetd: tcp/telnet: unknown service\n");
ea139302 145 exit(1);
31004941
GM
146 }
147 sin.sin_port = sp->s_port;
5d78ef73
GM
148 }
149
150 s = socket(AF_INET, SOCK_STREAM, 0);
151 if (s < 0) {
152 perror("telnetd: socket");;
153 exit(1);
154 }
ea139302
PB
155 (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
156 if (bind(s, (struct sockaddr *)&sin, sizeof sin) < 0) {
5d78ef73
GM
157 perror("bind");
158 exit(1);
159 }
160 if (listen(s, 1) < 0) {
161 perror("listen");
162 exit(1);
163 }
164 foo = sizeof sin;
ea139302 165 ns = accept(s, (struct sockaddr *)&sin, &foo);
5d78ef73
GM
166 if (ns < 0) {
167 perror("accept");
168 exit(1);
169 }
ea139302
PB
170 (void) dup2(ns, 0);
171 (void) close(ns);
172 (void) close(s);
5d78ef73 173 }
ea139302 174
076ae92c 175 openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON);
bb933cc2 176 fromlen = sizeof (from);
ea139302
PB
177 if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) {
178 fprintf(stderr, "%s: ", progname);
bb933cc2
MK
179 perror("getpeername");
180 _exit(1);
de3b21e8 181 }
bcb894cb 182 if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) {
3f99c0f7 183 syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
de3b21e8 184 }
ea139302
PB
185 net = 0;
186 doit(&from);
187 /* NOTREACHED */
188} /* end of main */
f553aca8 189
d8b5e42c 190int cleanup();
affdaa4e 191
d8b5e42c
GM
192/*
193 * getterminaltype
affdaa4e 194 *
ea139302 195 * Ask the other end to send along its terminal type and speed.
d8b5e42c 196 * Output is the variable terminaltype filled in.
affdaa4e 197 */
ea139302 198static char ttytype_sbbuf[] = { IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE };
d8b5e42c
GM
199void
200getterminaltype()
affdaa4e 201{
ea139302 202 void ttloop();
affdaa4e 203
ea139302
PB
204 settimer(baseline);
205 willoption(TELOPT_TTYPE, 1);
206 willoption(TELOPT_TSPEED, 1);
207 while ((hiswants[TELOPT_TTYPE] != hisopts[TELOPT_TTYPE]) ||
208 (hiswants[TELOPT_TSPEED] != hisopts[TELOPT_TSPEED])) {
d8b5e42c 209 ttloop();
affdaa4e 210 }
ea139302
PB
211 if (hisopts[TELOPT_TSPEED] == OPT_YES) {
212 static char sbbuf[] = { IAC, SB, TELOPT_TSPEED, TELQUAL_SEND, IAC, SE };
affdaa4e 213
d8b5e42c
GM
214 bcopy(sbbuf, nfrontp, sizeof sbbuf);
215 nfrontp += sizeof sbbuf;
ea139302
PB
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))
d8b5e42c 230 ttloop();
ea139302
PB
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 }
d8b5e42c
GM
260 }
261 }
ea139302
PB
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
273terminaltypeok(s)
274char *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);
d8b5e42c 291}
66b878f6
BJ
292
293/*
294 * Get a pty, scan input lines.
295 */
ea139302 296doit(who)
37c640e2 297 struct sockaddr_in *who;
66b878f6 298{
c29f876c 299 char *host, *inet_ntoa();
ea139302 300 int t;
37c640e2 301 struct hostent *hp;
1a33b848 302
ea139302
PB
303 /*
304 * Find an available pty to use.
305 */
306 pty = getpty();
307 if (pty < 0)
308 fatal(net, "All network ports in use");
c29f876c 309
ea139302
PB
310 t = getptyslave();
311
312 /* get name of connected client */
313 hp = gethostbyaddr((char *)&who->sin_addr, sizeof (struct in_addr),
37c640e2
SL
314 who->sin_family);
315 if (hp)
316 host = hp->h_name;
317 else
05fa5465 318 host = inet_ntoa(who->sin_addr);
d8b5e42c 319
d8b5e42c 320 /*
ea139302 321 * get terminal type.
d8b5e42c
GM
322 */
323 getterminaltype();
ea139302
PB
324 if (terminaltype == NULL)
325 terminaltype = "TERM=network";
d8b5e42c 326
affdaa4e 327 /*
ea139302 328 * Start up the login process on the slave side of the terminal
affdaa4e 329 */
ea139302 330 startslave(t, host);
5d78ef73 331
ea139302
PB
332 telnet(net, pty); /* begin server processing */
333 /*NOTREACHED*/
334} /* end of doit */
5d78ef73 335
ea139302
PB
336#ifndef MAXHOSTNAMELEN
337#define MAXHOSTNAMELEN 64
338#endif MAXHOSTNAMELEN
66b878f6
BJ
339/*
340 * Main loop. Select from pty and network, and
341 * hand data to telnet receiver finite state machine.
342 */
343telnet(f, p)
ea139302 344int f, p;
66b878f6
BJ
345{
346 int on = 1;
5eddff6d 347 char hostname[MAXHOSTNAMELEN];
ea139302
PB
348#ifdef CRAY2
349 int termstat();
350 int interrupt(), sendbrk();
351#endif
d0a64c71
GM
352#define TABBUFSIZ 512
353 char defent[TABBUFSIZ];
354 char defstrs[TABBUFSIZ];
355#undef TABBUFSIZ
356 char *HE;
357 char *HN;
358 char *IM;
ea139302
PB
359 void netflush();
360
9eebc521 361 /*
ea139302 362 * Initialize the slc mapping table.
9eebc521 363 */
ea139302 364 get_slc_defaults();
66b878f6 365
da96b661 366 /*
ea139302
PB
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.
da96b661 370 */
ea139302
PB
371 if (!myopts[TELOPT_SGA])
372 dooption(TELOPT_SGA);
affdaa4e
GM
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 */
ea139302
PB
383 willoption(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 willoption(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 willoption(TELOPT_NAWS, 1);
400 dooption(TELOPT_STATUS, 1);
401 flowmode = 1; /* default flow control state */
402 willoption(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
86b6dfea
PB
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, 0);
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 dooption(TELOPT_ECHO);
436
ea139302
PB
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 willoption(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);
affdaa4e 501
0c285f22
SL
502 /*
503 * Show banner that getty never gave.
10dc182f 504 *
d0a64c71
GM
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.
0c285f22 508 */
10dc182f 509
ea139302
PB
510 (void) gethostname(hostname, sizeof (hostname));
511
d0a64c71
GM
512 if (getent(defent, "default") == 1) {
513 char *getstr();
ea139302 514 char *cp=defstrs;
d0a64c71 515
ea139302
PB
516 HE = getstr("he", &cp);
517 HN = getstr("hn", &cp);
518 IM = getstr("im", &cp);
d0a64c71 519 if (HN && *HN)
ea139302
PB
520 (void) strcpy(hostname, HN);
521 if (IM == 0)
522 IM = "";
d0a64c71 523 } else {
ea139302
PB
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);
affdaa4e 540
66b878f6 541 for (;;) {
5d78ef73 542 fd_set ibits, obits, xbits;
66b878f6
BJ
543 register int c;
544
5d78ef73
GM
545 if (ncc < 0 && pcc < 0)
546 break;
547
ea139302
PB
548#ifdef CRAY2
549 if (needtermstat)
550 _termstat();
551#endif /* CRAY2 */
5d78ef73
GM
552 FD_ZERO(&ibits);
553 FD_ZERO(&obits);
554 FD_ZERO(&xbits);
66b878f6
BJ
555 /*
556 * Never look for input if there's still
557 * stuff in the corresponding output buffer
558 */
5d78ef73
GM
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 }
66b878f6
BJ
579 sleep(5);
580 continue;
581 }
582
5d78ef73
GM
583 /*
584 * Any urgent data?
585 */
586 if (FD_ISSET(net, &xbits)) {
587 SYNCHing = 1;
588 }
589
66b878f6
BJ
590 /*
591 * Something to read from the network...
592 */
5d78ef73 593 if (FD_ISSET(net, &ibits)) {
affdaa4e 594#if !defined(SO_OOBINLINE)
5d78ef73 595 /*
5eddff6d 596 * In 4.2 (and 4.3 beta) systems, the
5d78ef73
GM
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
ea139302 632 (void) ioctl(net, SIOCATMARK, (char *)&atmark);
5d78ef73
GM
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));
d8b5e42c 637 if (sequenceIs(didnetreceive, gotDM)) {
5d78ef73
GM
638 SYNCHing = stilloob(net);
639 }
640 }
641 } else {
642 ncc = read(net, netibuf, sizeof (netibuf));
66b878f6 643 }
5d78ef73
GM
644 } else {
645 ncc = read(net, netibuf, sizeof (netibuf));
646 }
647 settimer(didnetreceive);
affdaa4e 648#else /* !defined(SO_OOBINLINE)) */
5d78ef73 649 ncc = read(net, netibuf, sizeof (netibuf));
affdaa4e 650#endif /* !defined(SO_OOBINLINE)) */
5d78ef73
GM
651 if (ncc < 0 && errno == EWOULDBLOCK)
652 ncc = 0;
653 else {
654 if (ncc <= 0) {
655 break;
656 }
657 netip = netibuf;
658 }
66b878f6
BJ
659 }
660
661 /*
662 * Something to read from the pty...
663 */
ea139302 664 if (FD_ISSET(p, &ibits)) {
66b878f6
BJ
665 pcc = read(p, ptyibuf, BUFSIZ);
666 if (pcc < 0 && errno == EWOULDBLOCK)
667 pcc = 0;
668 else {
669 if (pcc <= 0)
670 break;
ea139302
PB
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
31004941 682 if (ptyibuf[0] & TIOCPKT_FLUSHWRITE) {
ea139302 683 netclear(); /* clear buffer back */
31004941
GM
684 *nfrontp++ = IAC;
685 *nfrontp++ = DM;
686 neturg = nfrontp-1; /* off by one XXX */
687 }
688 if (hisopts[TELOPT_LFLOW] &&
689 (ptyibuf[0] &
ea139302
PB
690 (TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))) {
691 (void) sprintf(nfrontp, "%c%c%c%c%c%c",
31004941
GM
692 IAC, SB, TELOPT_LFLOW,
693 ptyibuf[0] & TIOCPKT_DOSTOP ? 1 : 0,
694 IAC, SE);
695 nfrontp += 6;
696 }
4701a1c1
GM
697 pcc--;
698 ptyip = ptyibuf+1;
ea139302
PB
699#else /* CRAY2 */
700 if (!uselinemode) {
701 pcc = term_output(ptyibuf, ptyibuf2,
702 pcc, BUFSIZ);
703 ptyip = ptyibuf2;
704 } else
705 ptyip = ptyibuf;
706#endif /* CRAY2 */
707 }
4701a1c1 708 }
66b878f6
BJ
709
710 while (pcc > 0) {
711 if ((&netobuf[BUFSIZ] - nfrontp) < 2)
712 break;
713 c = *ptyip++ & 0377, pcc--;
714 if (c == IAC)
715 *nfrontp++ = c;
ea139302
PB
716#ifdef CRAY2
717 else if (c == '\n' &&
718 myopts[TELOPT_BINARY] == OPT_NO && newmap)
719 *nfrontp++ = '\r';
720#endif /* CRAY2 */
66b878f6 721 *nfrontp++ = c;
bdb993e7 722 if ((c == '\r') && (myopts[TELOPT_BINARY] == OPT_NO)) {
9f515693
GM
723 if (pcc > 0 && ((*ptyip & 0377) == '\n')) {
724 *nfrontp++ = *ptyip++ & 0377;
725 pcc--;
726 } else
727 *nfrontp++ = '\0';
728 }
66b878f6 729 }
5d78ef73 730 if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0)
66b878f6
BJ
731 netflush();
732 if (ncc > 0)
733 telrcv();
5d78ef73 734 if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0)
66b878f6
BJ
735 ptyflush();
736 }
737 cleanup();
ea139302 738} /* end of telnet */
66b878f6 739
ea139302
PB
740#ifndef TCSIG
741# ifdef TIOCSIG
742# define TCSIG TIOCSIG
743# endif
744#endif
66b878f6
BJ
745
746/*
747 * Send interrupt to process on other side of pty.
748 * If it is in raw mode, just write NULL;
749 * otherwise, write intr char.
750 */
751interrupt()
752{
66b878f6 753 ptyflush(); /* half-hearted */
ea139302
PB
754
755#ifdef TCSIG
756 (void) ioctl(pty, TCSIG, (char *)SIGINT);
757#else /* TCSIG */
758 init_termbuf();
759 *pfrontp++ = slctab[SLC_IP].sptr ? *slctab[SLC_IP].sptr : '\177';
760#endif /* TCSIG */
66b878f6
BJ
761}
762
a65453b7
GM
763/*
764 * Send quit to process on other side of pty.
765 * If it is in raw mode, just write NULL;
766 * otherwise, write quit char.
767 */
768sendbrk()
769{
a65453b7 770 ptyflush(); /* half-hearted */
ea139302
PB
771#ifdef TCSIG
772 (void) ioctl(pty, TCSIG, (char *)SIGQUIT);
773#else /* TCSIG */
774 init_termbuf();
775 *pfrontp++ = slctab[SLC_ABORT].sptr ? *slctab[SLC_ABORT].sptr : '\034';
776#endif /* TCSIG */
615dc3cd 777}
5d78ef73 778
ea139302 779sendsusp()
5d78ef73 780{
ea139302
PB
781#ifdef SIGTSTP
782 ptyflush(); /* half-hearted */
783# ifdef TCSIG
784 (void) ioctl(pty, TCSIG, (char *)SIGTSTP);
785# else /* TCSIG */
786 *pfrontp++ = slctab[SLC_SUSP].sptr ? *slctab[SLC_SUSP].sptr : '\032';
787# endif /* TCSIG */
788#endif /* SIGTSTP */
d0a64c71
GM
789}
790
ea139302 791doeof()
d0a64c71 792{
ea139302 793 init_termbuf();
d0a64c71 794
ea139302 795 *pfrontp++ = slctab[SLC_EOF].sptr ? *slctab[SLC_EOF].sptr : '\004';
d0a64c71 796}