Latest round of bug fixes. See the README file
[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 *
836fe169 5 * %sccs.include.redist.c%
8c5eec2f
DF
6 */
7
8#ifndef lint
9char copyright[] =
ea139302 10"@(#) Copyright (c) 1989 Regents of the University of California.\n\
8c5eec2f 11 All rights reserved.\n";
897ce52e 12#endif /* not lint */
8c5eec2f 13
ac6e6727 14#ifndef lint
2c9c7136 15static char sccsid[] = "@(#)telnetd.c 5.47 (Berkeley) %G%";
897ce52e 16#endif /* not lint */
ac6e6727 17
ea139302 18#include "telnetd.h"
2c9c7136 19#include "pathnames.h"
66b878f6
BJ
20
21/*
ea139302
PB
22 * I/O data buffers,
23 * pointers, and counters.
66b878f6
BJ
24 */
25char ptyibuf[BUFSIZ], *ptyip = ptyibuf;
ea139302 26char ptyibuf2[BUFSIZ];
affdaa4e 27
ea139302 28int hostinfo = 1; /* do we print login banner? */
affdaa4e 29
ea139302
PB
30#ifdef CRAY
31extern int newmap; /* nonzero if \n maps to ^M^J */
ed8f31c1 32int lowpty = 0, highpty; /* low, high pty numbers */
ea139302 33#endif /* CRAY */
affdaa4e 34
ea139302
PB
35int debug = 0;
36char *progname;
66b878f6 37
4a8a7128
PB
38#if defined(NEED_GETTOS)
39struct tosent {
40 char *t_name; /* name */
41 char **t_aliases; /* alias list */
42 char *t_proto; /* protocol */
43 int t_tos; /* Type Of Service bits */
44};
45
46struct tosent *
47gettosbyname(name, proto)
48char *name, *proto;
49{
50 static struct tosent te;
51 static char *aliasp = 0;
52
53 te.t_name = name;
54 te.t_aliases = &aliasp;
55 te.t_proto = proto;
56 te.t_tos = 020; /* Low Delay bit */
57 return(&te);
58}
59#endif
60
66b878f6
BJ
61main(argc, argv)
62 char *argv[];
63{
bb933cc2 64 struct sockaddr_in from;
bcb894cb 65 int on = 1, fromlen;
4a8a7128 66#if defined(HAS_IP_TOS) || defined(NEED_GETTOS)
ed8f31c1 67 struct tosent *tp;
4a8a7128 68#endif /* defined(HAS_IP_TOS) || defined(NEED_GETTOS) */
bb933cc2 69
ea139302
PB
70 pfrontp = pbackp = ptyobuf;
71 netip = netibuf;
72 nfrontp = nbackp = netobuf;
73
74 progname = *argv;
ed8f31c1
PB
75
76#ifdef CRAY
77 /*
78 * Get number of pty's before trying to process options,
79 * which may include changing pty range.
80 */
81 highpty = getnpty();
82#endif /* CRAY */
83
ea139302
PB
84top:
85 argc--, argv++;
2c9c7136
PB
86#ifdef convex
87 if (argc == 1 && !debug)
88 argc--; /* ignore the host/port name */
89#endif
ea139302
PB
90
91 if (argc > 0 && strcmp(*argv, "-debug") == 0) {
92 debug++;
93 goto top;
94 }
95
96#ifdef LINEMODE
97 if (argc > 0 && !strcmp(*argv, "-l")) {
98 alwayslinemode = 1;
99 goto top;
100 }
101#endif /* LINEMODE */
102
ea139302
PB
103 if (argc > 0 && !strcmp(*argv, "-h")) {
104 hostinfo = 0;
105 goto top;
106 }
107
2c9c7136 108#ifdef CRAY
ea139302
PB
109 if (argc > 0 && !strncmp(*argv, "-r", 2)) {
110 char *strchr();
111 char *c;
112
ed8f31c1
PB
113 /*
114 * Allow the specification of alterations to the pty search
115 * range. It is legal to specify only one, and not change the
116 * other from its default.
117 */
ea139302
PB
118 *argv += 2;
119 if (**argv == '\0' && argc)
120 argv++, argc--;
121 c = strchr(*argv, '-');
122 if (c) {
123 *c++ = '\0';
124 highpty = atoi(c);
ed8f31c1
PB
125 }
126 if (**argv != '\0')
127 lowpty = atoi(*argv);
128 if ((lowpty > highpty) || (lowpty < 0) || (highpty > 32767)) {
4a8a7128
PB
129 usage();
130 /* NOT REACHED */
ea139302
PB
131 }
132 goto top;
133 }
134# ifdef NEWINIT
135 if (argc > 0 && !strncmp(*argv, "-I", 2)) {
136 extern char *gen_id;
137
138 *argv += 2;
139 if (**argv == '\0') {
4a8a7128
PB
140 if (argc < 2) {
141 usage();
142 /* NOT REACHED */
143 }
ea139302 144 argv++, argc--;
4a8a7128
PB
145 if (**argv == '\0') {
146 usage();
147 /* NOT REACHED */
148 }
ea139302
PB
149 }
150 gen_id = *argv;
151 goto top;
152 }
153# endif /* NEWINIT */
154#endif /* CRAY */
155
4a8a7128
PB
156#ifdef DIAGNOSTICS
157 /*
158 * Check for desired diagnostics capabilities.
159 */
160 if (argc > 0 && !strncmp(*argv, "-D", 2)) {
161 *argv += 2;
162 if (**argv == '\0') {
163 if (argc < 2) {
164 usage();
165 /* NOT REACHED */
166 }
167 argv++, argc--;
168 if (**argv == '\0') {
169 usage();
170 /* NOT REACHED */
171 }
172 }
173 if (!strcmp(*argv, "report")) {
174 diagnostic |= TD_REPORT|TD_OPTIONS;
175 } else if (!strcmp(*argv, "exercise")) {
176 diagnostic |= TD_EXERCISE;
177 } else if (!strcmp(*argv, "netdata")) {
178 diagnostic |= TD_NETDATA;
179 } else if (!strcmp(*argv, "ptydata")) {
180 diagnostic |= TD_PTYDATA;
181 } else if (!strcmp(*argv, "options")) {
182 diagnostic |= TD_OPTIONS;
183 } else {
184 usage();
185 /* NOT REACHED */
186 }
187 goto top;
188 }
189#endif /* DIAGNOSTICS */
190
191#ifdef BFTPDAEMON
192 /*
193 * Check for bftp daemon
194 */
195 if (argc > 0 && !strncmp(*argv, "-B", 2)) {
196 bftpd++;
197 goto top;
198 }
199#endif /* BFTPDAEMON */
200
201 if (argc > 0 && **argv == '-') {
202 fprintf(stderr, "telnetd: %s: unknown option\n", *argv+1);
203 usage();
204 /* NOT REACHED */
205 }
206
ea139302 207 if (debug) {
5d78ef73
GM
208 int s, ns, foo;
209 struct servent *sp;
210 static struct sockaddr_in sin = { AF_INET };
211
4a8a7128
PB
212 if (argc > 1) {
213 usage();
214 /* NOT REACHED */
215 } else if (argc == 1) {
ea139302
PB
216 if (sp = getservbyname(*argv, "tcp")) {
217 sin.sin_port = sp->s_port;
218 } else {
219 sin.sin_port = atoi(*argv);
220 if ((int)sin.sin_port <= 0) {
221 fprintf(stderr, "telnetd: %s: bad port #\n", *argv);
4a8a7128
PB
222 usage();
223 /* NOT REACHED */
ea139302
PB
224 }
225 sin.sin_port = htons((u_short)sin.sin_port);
226 }
31004941
GM
227 } else {
228 sp = getservbyname("telnet", "tcp");
229 if (sp == 0) {
4a8a7128 230 fprintf(stderr, "telnetd: tcp/telnet: unknown service\n");
ea139302 231 exit(1);
31004941
GM
232 }
233 sin.sin_port = sp->s_port;
5d78ef73
GM
234 }
235
236 s = socket(AF_INET, SOCK_STREAM, 0);
237 if (s < 0) {
238 perror("telnetd: socket");;
239 exit(1);
240 }
ea139302
PB
241 (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
242 if (bind(s, (struct sockaddr *)&sin, sizeof sin) < 0) {
5d78ef73
GM
243 perror("bind");
244 exit(1);
245 }
246 if (listen(s, 1) < 0) {
247 perror("listen");
248 exit(1);
249 }
250 foo = sizeof sin;
ea139302 251 ns = accept(s, (struct sockaddr *)&sin, &foo);
5d78ef73
GM
252 if (ns < 0) {
253 perror("accept");
254 exit(1);
255 }
ea139302
PB
256 (void) dup2(ns, 0);
257 (void) close(ns);
258 (void) close(s);
4a8a7128
PB
259 } else if (argc > 0) {
260 usage();
261 /* NOT REACHED */
5d78ef73 262 }
ea139302 263
076ae92c 264 openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON);
bb933cc2 265 fromlen = sizeof (from);
ea139302
PB
266 if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) {
267 fprintf(stderr, "%s: ", progname);
bb933cc2
MK
268 perror("getpeername");
269 _exit(1);
de3b21e8 270 }
bcb894cb 271 if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) {
3f99c0f7 272 syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
de3b21e8 273 }
ed8f31c1 274
4a8a7128
PB
275#if defined(HAS_IP_TOS) || defined(NEED_GETTOS)
276 if ((tp = gettosbyname("telnet", "tcp")) &&
277 (setsockopt(0, IPPROTO_IP, IP_TOS, &tp->t_tos, sizeof(int)) < 0))
ed8f31c1 278 syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
4a8a7128 279#endif /* defined(HAS_IP_TOS) || defined(NEED_GETTOS) */
ea139302
PB
280 net = 0;
281 doit(&from);
282 /* NOTREACHED */
283} /* end of main */
f553aca8 284
4a8a7128
PB
285usage()
286{
287 fprintf(stderr, "Usage: telnetd [-debug] [-h]");
288#ifdef NEWINIT
289 fprintf(stderr, " [-Iinitid]");
290#endif /* NEWINIT */
291#ifdef DIAGNOSTICS
292 fprintf(stderr, " [-D (options|report|exercise|netdata|ptydata)]");
293#endif /* DIAGNOSTICS */
294#ifdef LINEMODE
295 fprintf(stderr, " [-l]");
296#endif
297#ifdef CRAY
298 fprintf(stderr, " [-r[lowpty]-[highpty]]");
299#endif
300#ifdef BFTPDAEMON
301 fprintf(stderr, " [-B]");
302#endif /* BFTPDAEMON */
303 fprintf(stderr, " [port]\n");
304 exit(1);
305}
306
ed8f31c1 307void cleanup();
affdaa4e 308
d8b5e42c
GM
309/*
310 * getterminaltype
affdaa4e 311 *
ea139302 312 * Ask the other end to send along its terminal type and speed.
d8b5e42c 313 * Output is the variable terminaltype filled in.
affdaa4e 314 */
ea139302 315static char ttytype_sbbuf[] = { IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE };
d8b5e42c
GM
316void
317getterminaltype()
affdaa4e 318{
ea139302 319 void ttloop();
affdaa4e 320
ea139302 321 settimer(baseline);
053fd49d
PB
322 send_do(TELOPT_TTYPE, 1);
323 send_do(TELOPT_TSPEED, 1);
4a8a7128
PB
324 send_do(TELOPT_XDISPLOC, 1);
325 send_do(TELOPT_ENVIRON, 1);
326 while (his_will_wont_is_changing(TELOPT_TTYPE) ||
327 his_will_wont_is_changing(TELOPT_TSPEED) ||
328 his_will_wont_is_changing(TELOPT_XDISPLOC) ||
329 his_will_wont_is_changing(TELOPT_ENVIRON)) {
d8b5e42c 330 ttloop();
affdaa4e 331 }
4a8a7128 332 if (his_state_is_will(TELOPT_TSPEED)) {
ea139302 333 static char sbbuf[] = { IAC, SB, TELOPT_TSPEED, TELQUAL_SEND, IAC, SE };
affdaa4e 334
d8b5e42c
GM
335 bcopy(sbbuf, nfrontp, sizeof sbbuf);
336 nfrontp += sizeof sbbuf;
ea139302 337 }
4a8a7128
PB
338 if (his_state_is_will(TELOPT_XDISPLOC)) {
339 static char sbbuf[] = { IAC, SB, TELOPT_XDISPLOC, TELQUAL_SEND, IAC, SE };
340
341 bcopy(sbbuf, nfrontp, sizeof sbbuf);
342 nfrontp += sizeof sbbuf;
343 }
344 if (his_state_is_will(TELOPT_ENVIRON)) {
345 static char sbbuf[] = { IAC, SB, TELOPT_ENVIRON, TELQUAL_SEND, IAC, SE };
346
347 bcopy(sbbuf, nfrontp, sizeof sbbuf);
348 nfrontp += sizeof sbbuf;
349 }
350 if (his_state_is_will(TELOPT_TTYPE)) {
ea139302
PB
351
352 bcopy(ttytype_sbbuf, nfrontp, sizeof ttytype_sbbuf);
353 nfrontp += sizeof ttytype_sbbuf;
354 }
4a8a7128 355 if (his_state_is_will(TELOPT_TSPEED)) {
ea139302
PB
356 while (sequenceIs(tspeedsubopt, baseline))
357 ttloop();
358 }
4a8a7128
PB
359 if (his_state_is_will(TELOPT_XDISPLOC)) {
360 while (sequenceIs(xdisplocsubopt, baseline))
361 ttloop();
362 }
363 if (his_state_is_will(TELOPT_ENVIRON)) {
364 while (sequenceIs(environsubopt, baseline))
365 ttloop();
366 }
367 if (his_state_is_will(TELOPT_TTYPE)) {
ea139302
PB
368 char first[256], last[256];
369
370 while (sequenceIs(ttypesubopt, baseline))
d8b5e42c 371 ttloop();
ea139302 372
4a8a7128
PB
373 /*
374 * If the other side has already disabled the option, then
375 * we have to just go with what we (might) have already gotten.
376 */
377 if (his_state_is_will(TELOPT_TTYPE) && !terminaltypeok(terminaltype)) {
ea139302
PB
378 (void) strncpy(first, terminaltype, sizeof(first));
379 for(;;) {
380 /*
381 * Save the unknown name, and request the next name.
382 */
383 (void) strncpy(last, terminaltype, sizeof(last));
384 _gettermname();
4a8a7128 385 if (terminaltypeok(terminaltype))
ea139302 386 break;
4a8a7128
PB
387 if ((strncmp(last, terminaltype, sizeof(last)) == 0) ||
388 his_state_is_wont(TELOPT_TTYPE)) {
ea139302
PB
389 /*
390 * We've hit the end. If this is the same as
391 * the first name, just go with it.
392 */
2c9c7136 393 if (strncmp(first, terminaltype, sizeof(first)) == 0)
ea139302
PB
394 break;
395 /*
4a8a7128 396 * Get the terminal name one more time, so that
ea139302
PB
397 * RFC1091 compliant telnets will cycle back to
398 * the start of the list.
399 */
4a8a7128 400 _gettermname();
2c9c7136 401 if (strncmp(first, terminaltype, sizeof(first)) != 0)
ea139302
PB
402 (void) strncpy(terminaltype, first, sizeof(first));
403 break;
404 }
405 }
d8b5e42c
GM
406 }
407 }
ea139302
PB
408} /* end of getterminaltype */
409
410_gettermname()
411{
4a8a7128
PB
412 /*
413 * If the client turned off the option,
414 * we can't send another request, so we
415 * just return.
416 */
417 if (his_state_is_wont(TELOPT_TTYPE))
418 return;
ea139302
PB
419 settimer(baseline);
420 bcopy(ttytype_sbbuf, nfrontp, sizeof ttytype_sbbuf);
421 nfrontp += sizeof ttytype_sbbuf;
422 while (sequenceIs(ttypesubopt, baseline))
423 ttloop();
424}
425
426terminaltypeok(s)
427char *s;
428{
429 char buf[1024];
430
431 if (terminaltype == NULL)
432 return(1);
433
434 /*
435 * tgetent() will return 1 if the type is known, and
436 * 0 if it is not known. If it returns -1, it couldn't
437 * open the database. But if we can't open the database,
438 * it won't help to say we failed, because we won't be
439 * able to verify anything else. So, we treat -1 like 1.
440 */
441 if (tgetent(buf, s) == 0)
442 return(0);
443 return(1);
d8b5e42c 444}
66b878f6
BJ
445
446/*
447 * Get a pty, scan input lines.
448 */
ea139302 449doit(who)
37c640e2 450 struct sockaddr_in *who;
66b878f6 451{
c29f876c 452 char *host, *inet_ntoa();
ea139302 453 int t;
37c640e2 454 struct hostent *hp;
1a33b848 455
ea139302
PB
456 /*
457 * Find an available pty to use.
458 */
2c9c7136 459#ifndef convex
ea139302
PB
460 pty = getpty();
461 if (pty < 0)
462 fatal(net, "All network ports in use");
2c9c7136
PB
463#else
464 for (;;) {
465 char *lp;
466 extern char *line, *getpty();
c29f876c 467
2c9c7136
PB
468 if ((lp = getpty()) == NULL)
469 fatal(net, "Out of ptys");
470
471 if ((pty = open(lp, 2)) >= 0) {
472 strcpy(line,lp);
473 line[5] = 't';
474 break;
475 }
476 }
9633556e 477#endif
ea139302
PB
478
479 /* get name of connected client */
480 hp = gethostbyaddr((char *)&who->sin_addr, sizeof (struct in_addr),
37c640e2
SL
481 who->sin_family);
482 if (hp)
483 host = hp->h_name;
484 else
05fa5465 485 host = inet_ntoa(who->sin_addr);
d8b5e42c 486
4a8a7128 487 init_env();
d8b5e42c 488 /*
ea139302 489 * get terminal type.
d8b5e42c
GM
490 */
491 getterminaltype();
4a8a7128 492 setenv("TERM", terminaltype ? terminaltype : "network", 1);
d8b5e42c 493
affdaa4e 494 /*
ea139302 495 * Start up the login process on the slave side of the terminal
affdaa4e 496 */
2c9c7136
PB
497#ifndef convex
498 startslave(host);
5d78ef73 499
ea139302 500 telnet(net, pty); /* begin server processing */
2c9c7136
PB
501#else
502 telnet(net, pty, host);
503#endif
ea139302
PB
504 /*NOTREACHED*/
505} /* end of doit */
5d78ef73 506
ea139302
PB
507#ifndef MAXHOSTNAMELEN
508#define MAXHOSTNAMELEN 64
509#endif MAXHOSTNAMELEN
66b878f6
BJ
510/*
511 * Main loop. Select from pty and network, and
512 * hand data to telnet receiver finite state machine.
513 */
2c9c7136 514#ifndef convex
66b878f6 515telnet(f, p)
2c9c7136
PB
516#else
517telnet(f, p, host)
518#endif
ea139302 519int f, p;
2c9c7136
PB
520#ifdef convex
521char *host;
522#endif
66b878f6
BJ
523{
524 int on = 1;
5eddff6d 525 char hostname[MAXHOSTNAMELEN];
ed8f31c1 526#if defined(CRAY2) && defined(UNICOS5)
ea139302
PB
527 int termstat();
528 int interrupt(), sendbrk();
529#endif
d0a64c71
GM
530#define TABBUFSIZ 512
531 char defent[TABBUFSIZ];
532 char defstrs[TABBUFSIZ];
533#undef TABBUFSIZ
534 char *HE;
535 char *HN;
536 char *IM;
ea139302
PB
537 void netflush();
538
9eebc521 539 /*
ea139302 540 * Initialize the slc mapping table.
9eebc521 541 */
ea139302 542 get_slc_defaults();
66b878f6 543
da96b661 544 /*
ea139302
PB
545 * Do some tests where it is desireable to wait for a response.
546 * Rather than doing them slowly, one at a time, do them all
547 * at once.
da96b661 548 */
4a8a7128 549 if (my_state_is_wont(TELOPT_SGA))
053fd49d 550 send_will(TELOPT_SGA, 1);
affdaa4e
GM
551 /*
552 * Is the client side a 4.2 (NOT 4.3) system? We need to know this
553 * because 4.2 clients are unable to deal with TCP urgent data.
554 *
555 * To find out, we send out a "DO ECHO". If the remote system
556 * answers "WILL ECHO" it is probably a 4.2 client, and we note
557 * that fact ("WILL ECHO" ==> that the client will echo what
558 * WE, the server, sends it; it does NOT mean that the client will
559 * echo the terminal input).
560 */
053fd49d 561 send_do(TELOPT_ECHO, 1);
ea139302
PB
562
563#ifdef LINEMODE
4a8a7128 564 if (his_state_is_wont(TELOPT_LINEMODE)) {
ea139302
PB
565 /* Query the peer for linemode support by trying to negotiate
566 * the linemode option.
567 */
4a8a7128 568 linemode = 0;
ea139302 569 editmode = 0;
053fd49d 570 send_do(TELOPT_LINEMODE, 1); /* send do linemode */
ea139302
PB
571 }
572#endif /* LINEMODE */
573
574 /*
575 * Send along a couple of other options that we wish to negotiate.
576 */
053fd49d
PB
577 send_do(TELOPT_NAWS, 1);
578 send_will(TELOPT_STATUS, 1);
ea139302 579 flowmode = 1; /* default flow control state */
053fd49d 580 send_do(TELOPT_LFLOW, 1);
ea139302
PB
581
582 /*
583 * Spin, waiting for a response from the DO ECHO. However,
584 * some REALLY DUMB telnets out there might not respond
585 * to the DO ECHO. So, we spin looking for NAWS, (most dumb
586 * telnets so far seem to respond with WONT for a DO that
587 * they don't understand...) because by the time we get the
588 * response, it will already have processed the DO ECHO.
589 * Kludge upon kludge.
590 */
4a8a7128 591 while (his_will_wont_is_changing(TELOPT_NAWS))
ea139302
PB
592 ttloop();
593
4a8a7128
PB
594 /*
595 * But...
596 * The client might have sent a WILL NAWS as part of its
597 * startup code; if so, we'll be here before we get the
598 * response to the DO ECHO. We'll make the assumption
599 * that any implementation that understands about NAWS
600 * is a modern enough implementation that it will respond
601 * to our DO ECHO request; hence we'll do another spin
602 * waiting for the ECHO option to settle down, which is
603 * what we wanted to do in the first place...
604 */
605 if (his_want_state_is_will(TELOPT_ECHO) &&
606 his_state_is_will(TELOPT_NAWS)) {
607 while (his_will_wont_is_changing(TELOPT_ECHO))
608 ttloop();
609 }
86b6dfea
PB
610 /*
611 * On the off chance that the telnet client is broken and does not
612 * respond to the DO ECHO we sent, (after all, we did send the
613 * DO NAWS negotiation after the DO ECHO, and we won't get here
614 * until a response to the DO NAWS comes back) simulate the
615 * receipt of a will echo. This will also send a WONT ECHO
616 * to the client, since we assume that the client failed to
617 * respond because it believes that it is already in DO ECHO
618 * mode, which we do not want.
619 */
4a8a7128
PB
620 if (his_want_state_is_will(TELOPT_ECHO)) {
621#ifdef DIAGNOSTICS
622 if (diagnostic & TD_OPTIONS) {
623 sprintf(nfrontp, "td: simulating recv\r\n");
624 nfrontp += strlen(nfrontp);
625 }
626#endif /* DIAGNOSTICS */
053fd49d 627 willoption(TELOPT_ECHO);
ed8f31c1 628 }
86b6dfea
PB
629
630 /*
631 * Finally, to clean things up, we turn on our echo. This
632 * will break stupid 4.2 telnets out of local terminal echo.
633 */
634
4a8a7128 635 if (my_state_is_wont(TELOPT_ECHO))
053fd49d 636 send_will(TELOPT_ECHO, 1);
86b6dfea 637
ea139302 638 /*
2c9c7136 639 * Turn on packet mode
ea139302
PB
640 */
641 (void) ioctl(p, TIOCPKT, (char *)&on);
2c9c7136 642#ifdef KLUDGELINEMODE
ea139302
PB
643 /*
644 * Continuing line mode support. If client does not support
645 * real linemode, attempt to negotiate kludge linemode by sending
646 * the do timing mark sequence.
647 */
648 if (lmodetype < REAL_LINEMODE)
053fd49d 649 send_do(TELOPT_TM, 1);
2c9c7136 650#endif /* KLUDGELINEMODE */
ea139302
PB
651
652 /*
653 * Call telrcv() once to pick up anything received during
654 * terminal type negotiation, 4.2/4.3 determination, and
655 * linemode negotiation.
656 */
657 telrcv();
658
659 (void) ioctl(f, FIONBIO, (char *)&on);
660 (void) ioctl(p, FIONBIO, (char *)&on);
ed8f31c1 661#if defined(CRAY2) && defined(UNICOS5)
ea139302
PB
662 init_termdriver(f, p, interrupt, sendbrk);
663#endif
664
665#if defined(SO_OOBINLINE)
666 (void) setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on);
667#endif /* defined(SO_OOBINLINE) */
668
669#ifdef SIGTSTP
670 (void) signal(SIGTSTP, SIG_IGN);
671#endif
672#ifdef SIGTTOU
673 /*
674 * Ignoring SIGTTOU keeps the kernel from blocking us
675 * in ttioct() in /sys/tty.c.
676 */
677 (void) signal(SIGTTOU, SIG_IGN);
678#endif
679
680 (void) signal(SIGCHLD, cleanup);
681
ed8f31c1 682#if defined(CRAY2) && defined(UNICOS5)
ea139302
PB
683 /*
684 * Cray-2 will send a signal when pty modes are changed by slave
685 * side. Set up signal handler now.
686 */
687 if ((int)signal(SIGUSR1, termstat) < 0)
688 perror("signal");
689 else if (ioctl(p, TCSIGME, (char *)SIGUSR1) < 0)
690 perror("ioctl:TCSIGME");
691 /*
692 * Make processing loop check terminal characteristics early on.
693 */
694 termstat();
695#endif
696
2c9c7136
PB
697#ifdef TIOCNOTTY
698 {
699 register int t;
700 t = open(_PATH_TTY, O_RDWR);
701 if (t >= 0) {
702 (void) ioctl(t, TIOCNOTTY, (char *)0);
703 (void) close(t);
704 }
705 }
4a8a7128 706#endif
2c9c7136 707
4a8a7128 708#if defined(TIOCSCTTY) && defined(CRAY)
2c9c7136 709 (void) setsid();
4a8a7128 710 ioctl(p, TIOCSCTTY, 0);
ed8f31c1 711#endif
affdaa4e 712
0c285f22
SL
713 /*
714 * Show banner that getty never gave.
10dc182f 715 *
d0a64c71
GM
716 * We put the banner in the pty input buffer. This way, it
717 * gets carriage return null processing, etc., just like all
718 * other pty --> client data.
0c285f22 719 */
10dc182f 720
2c9c7136
PB
721#if !defined(CRAY) || !defined(NEWINIT)
722 if (getenv("USER"))
723 hostinfo = 0;
724#endif
ea139302
PB
725 (void) gethostname(hostname, sizeof (hostname));
726
d0a64c71
GM
727 if (getent(defent, "default") == 1) {
728 char *getstr();
ea139302 729 char *cp=defstrs;
d0a64c71 730
ea139302
PB
731 HE = getstr("he", &cp);
732 HN = getstr("hn", &cp);
733 IM = getstr("im", &cp);
d0a64c71 734 if (HN && *HN)
ea139302
PB
735 (void) strcpy(hostname, HN);
736 if (IM == 0)
737 IM = "";
d0a64c71 738 } else {
2c9c7136 739 IM = DEFAULT_IM;
ea139302
PB
740 HE = 0;
741 }
742 edithost(HE, hostname);
2c9c7136 743 if (hostinfo && *IM)
ea139302
PB
744 putf(IM, ptyibuf2);
745
746 if (pcc)
747 (void) strncat(ptyibuf2, ptyip, pcc+1);
748 ptyip = ptyibuf2;
749 pcc = strlen(ptyip);
ed8f31c1
PB
750#ifdef LINEMODE
751 /*
752 * Last check to make sure all our states are correct.
753 */
754 init_termbuf();
755 localstat();
756#endif /* LINEMODE */
affdaa4e 757
4a8a7128
PB
758#ifdef DIAGNOSTICS
759 if (diagnostic & TD_REPORT) {
760 sprintf(nfrontp, "td: Entering processing loop\r\n");
761 nfrontp += strlen(nfrontp);
762 }
763#endif /* DIAGNOSTICS */
764
2c9c7136
PB
765#ifdef convex
766 startslave(host);
767#endif
768
66b878f6 769 for (;;) {
5d78ef73 770 fd_set ibits, obits, xbits;
66b878f6
BJ
771 register int c;
772
5d78ef73
GM
773 if (ncc < 0 && pcc < 0)
774 break;
775
ed8f31c1 776#if defined(CRAY2) && defined(UNICOS5)
ea139302
PB
777 if (needtermstat)
778 _termstat();
ed8f31c1 779#endif /* defined(CRAY2) && defined(UNICOS5) */
5d78ef73
GM
780 FD_ZERO(&ibits);
781 FD_ZERO(&obits);
782 FD_ZERO(&xbits);
66b878f6
BJ
783 /*
784 * Never look for input if there's still
785 * stuff in the corresponding output buffer
786 */
5d78ef73
GM
787 if (nfrontp - nbackp || pcc > 0) {
788 FD_SET(f, &obits);
789 } else {
790 FD_SET(p, &ibits);
791 }
792 if (pfrontp - pbackp || ncc > 0) {
793 FD_SET(p, &obits);
794 } else {
795 FD_SET(f, &ibits);
796 }
797 if (!SYNCHing) {
798 FD_SET(f, &xbits);
799 }
800 if ((c = select(16, &ibits, &obits, &xbits,
801 (struct timeval *)0)) < 1) {
802 if (c == -1) {
803 if (errno == EINTR) {
804 continue;
805 }
806 }
66b878f6
BJ
807 sleep(5);
808 continue;
809 }
810
5d78ef73
GM
811 /*
812 * Any urgent data?
813 */
814 if (FD_ISSET(net, &xbits)) {
815 SYNCHing = 1;
816 }
817
66b878f6
BJ
818 /*
819 * Something to read from the network...
820 */
5d78ef73 821 if (FD_ISSET(net, &ibits)) {
affdaa4e 822#if !defined(SO_OOBINLINE)
5d78ef73 823 /*
5eddff6d 824 * In 4.2 (and 4.3 beta) systems, the
5d78ef73
GM
825 * OOB indication and data handling in the kernel
826 * is such that if two separate TCP Urgent requests
827 * come in, one byte of TCP data will be overlaid.
828 * This is fatal for Telnet, but we try to live
829 * with it.
830 *
831 * In addition, in 4.2 (and...), a special protocol
832 * is needed to pick up the TCP Urgent data in
833 * the correct sequence.
834 *
835 * What we do is: if we think we are in urgent
836 * mode, we look to see if we are "at the mark".
837 * If we are, we do an OOB receive. If we run
838 * this twice, we will do the OOB receive twice,
839 * but the second will fail, since the second
840 * time we were "at the mark", but there wasn't
841 * any data there (the kernel doesn't reset
842 * "at the mark" until we do a normal read).
843 * Once we've read the OOB data, we go ahead
844 * and do normal reads.
845 *
846 * There is also another problem, which is that
847 * since the OOB byte we read doesn't put us
848 * out of OOB state, and since that byte is most
849 * likely the TELNET DM (data mark), we would
850 * stay in the TELNET SYNCH (SYNCHing) state.
851 * So, clocks to the rescue. If we've "just"
852 * received a DM, then we test for the
853 * presence of OOB data when the receive OOB
854 * fails (and AFTER we did the normal mode read
855 * to clear "at the mark").
856 */
857 if (SYNCHing) {
858 int atmark;
859
ea139302 860 (void) ioctl(net, SIOCATMARK, (char *)&atmark);
5d78ef73
GM
861 if (atmark) {
862 ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB);
863 if ((ncc == -1) && (errno == EINVAL)) {
864 ncc = read(net, netibuf, sizeof (netibuf));
d8b5e42c 865 if (sequenceIs(didnetreceive, gotDM)) {
5d78ef73
GM
866 SYNCHing = stilloob(net);
867 }
868 }
869 } else {
870 ncc = read(net, netibuf, sizeof (netibuf));
66b878f6 871 }
5d78ef73
GM
872 } else {
873 ncc = read(net, netibuf, sizeof (netibuf));
874 }
875 settimer(didnetreceive);
affdaa4e 876#else /* !defined(SO_OOBINLINE)) */
5d78ef73 877 ncc = read(net, netibuf, sizeof (netibuf));
affdaa4e 878#endif /* !defined(SO_OOBINLINE)) */
5d78ef73
GM
879 if (ncc < 0 && errno == EWOULDBLOCK)
880 ncc = 0;
881 else {
882 if (ncc <= 0) {
883 break;
884 }
885 netip = netibuf;
886 }
4a8a7128
PB
887#ifdef DIAGNOSTICS
888 if (diagnostic & (TD_REPORT | TD_NETDATA)) {
889 sprintf(nfrontp, "td: netread %d chars\r\n", ncc);
890 nfrontp += strlen(nfrontp);
891 }
892 if (diagnostic & TD_NETDATA) {
893 printdata("nd", netip, ncc);
894 }
895#endif /* DIAGNOSTICS */
66b878f6
BJ
896 }
897
898 /*
899 * Something to read from the pty...
900 */
ea139302 901 if (FD_ISSET(p, &ibits)) {
66b878f6
BJ
902 pcc = read(p, ptyibuf, BUFSIZ);
903 if (pcc < 0 && errno == EWOULDBLOCK)
904 pcc = 0;
905 else {
906 if (pcc <= 0)
907 break;
ed8f31c1 908#if !defined(CRAY2) || !defined(UNICOS5)
ea139302
PB
909#ifdef LINEMODE
910 /*
911 * If ioctl from pty, pass it through net
912 */
913 if (ptyibuf[0] & TIOCPKT_IOCTL) {
914 copy_termbuf(ptyibuf+1, pcc-1);
915 localstat();
916 pcc = 1;
917 }
918#endif LINEMODE
31004941 919 if (ptyibuf[0] & TIOCPKT_FLUSHWRITE) {
ea139302 920 netclear(); /* clear buffer back */
2c9c7136 921#ifndef NO_URGENT
ed8f31c1 922 /*
2c9c7136 923 * There are client telnets on some
ed8f31c1
PB
924 * operating systems get screwed up
925 * royally if we send them urgent
2c9c7136 926 * mode data.
ed8f31c1 927 */
31004941
GM
928 *nfrontp++ = IAC;
929 *nfrontp++ = DM;
930 neturg = nfrontp-1; /* off by one XXX */
ed8f31c1 931#endif
31004941 932 }
4a8a7128 933 if (his_state_is_will(TELOPT_LFLOW) &&
31004941 934 (ptyibuf[0] &
ea139302
PB
935 (TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))) {
936 (void) sprintf(nfrontp, "%c%c%c%c%c%c",
31004941
GM
937 IAC, SB, TELOPT_LFLOW,
938 ptyibuf[0] & TIOCPKT_DOSTOP ? 1 : 0,
939 IAC, SE);
940 nfrontp += 6;
941 }
4701a1c1
GM
942 pcc--;
943 ptyip = ptyibuf+1;
ed8f31c1 944#else /* defined(CRAY2) && defined(UNICOS5) */
ea139302 945 if (!uselinemode) {
cb781470
PB
946 unpcc = pcc;
947 unptyip = ptyibuf;
948 pcc = term_output(&unptyip, ptyibuf2,
949 &unpcc, BUFSIZ);
ea139302
PB
950 ptyip = ptyibuf2;
951 } else
952 ptyip = ptyibuf;
ed8f31c1 953#endif /* defined(CRAY2) && defined(UNICOS5) */
ea139302 954 }
4701a1c1 955 }
66b878f6
BJ
956
957 while (pcc > 0) {
958 if ((&netobuf[BUFSIZ] - nfrontp) < 2)
959 break;
960 c = *ptyip++ & 0377, pcc--;
961 if (c == IAC)
962 *nfrontp++ = c;
ed8f31c1 963#if defined(CRAY2) && defined(UNICOS5)
ea139302 964 else if (c == '\n' &&
4a8a7128 965 my_state_is_wont(TELOPT_BINARY) && newmap)
ea139302 966 *nfrontp++ = '\r';
ed8f31c1 967#endif /* defined(CRAY2) && defined(UNICOS5) */
66b878f6 968 *nfrontp++ = c;
4a8a7128 969 if ((c == '\r') && (my_state_is_wont(TELOPT_BINARY))) {
9f515693
GM
970 if (pcc > 0 && ((*ptyip & 0377) == '\n')) {
971 *nfrontp++ = *ptyip++ & 0377;
972 pcc--;
973 } else
974 *nfrontp++ = '\0';
975 }
66b878f6 976 }
ed8f31c1 977#if defined(CRAY2) && defined(UNICOS5)
cb781470
PB
978 /*
979 * If chars were left over from the terminal driver,
980 * note their existence.
981 */
982 if (!uselinemode && unpcc) {
983 pcc = unpcc;
984 unpcc = 0;
985 ptyip = unptyip;
986 }
ed8f31c1 987#endif /* defined(CRAY2) && defined(UNICOS5) */
cb781470 988
5d78ef73 989 if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0)
66b878f6
BJ
990 netflush();
991 if (ncc > 0)
992 telrcv();
5d78ef73 993 if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0)
66b878f6
BJ
994 ptyflush();
995 }
996 cleanup();
ea139302 997} /* end of telnet */
66b878f6 998
ea139302
PB
999#ifndef TCSIG
1000# ifdef TIOCSIG
1001# define TCSIG TIOCSIG
1002# endif
1003#endif
66b878f6
BJ
1004
1005/*
1006 * Send interrupt to process on other side of pty.
1007 * If it is in raw mode, just write NULL;
1008 * otherwise, write intr char.
1009 */
1010interrupt()
1011{
66b878f6 1012 ptyflush(); /* half-hearted */
ea139302
PB
1013
1014#ifdef TCSIG
1015 (void) ioctl(pty, TCSIG, (char *)SIGINT);
1016#else /* TCSIG */
1017 init_termbuf();
ed8f31c1
PB
1018 *pfrontp++ = slctab[SLC_IP].sptr ?
1019 (unsigned char)*slctab[SLC_IP].sptr : '\177';
ea139302 1020#endif /* TCSIG */
66b878f6
BJ
1021}
1022
a65453b7
GM
1023/*
1024 * Send quit to process on other side of pty.
1025 * If it is in raw mode, just write NULL;
1026 * otherwise, write quit char.
1027 */
1028sendbrk()
1029{
a65453b7 1030 ptyflush(); /* half-hearted */
ea139302
PB
1031#ifdef TCSIG
1032 (void) ioctl(pty, TCSIG, (char *)SIGQUIT);
1033#else /* TCSIG */
1034 init_termbuf();
ed8f31c1
PB
1035 *pfrontp++ = slctab[SLC_ABORT].sptr ?
1036 (unsigned char)*slctab[SLC_ABORT].sptr : '\034';
ea139302 1037#endif /* TCSIG */
615dc3cd 1038}
5d78ef73 1039
ea139302 1040sendsusp()
5d78ef73 1041{
ea139302
PB
1042#ifdef SIGTSTP
1043 ptyflush(); /* half-hearted */
1044# ifdef TCSIG
1045 (void) ioctl(pty, TCSIG, (char *)SIGTSTP);
1046# else /* TCSIG */
ed8f31c1
PB
1047 *pfrontp++ = slctab[SLC_SUSP].sptr ?
1048 (unsigned char)*slctab[SLC_SUSP].sptr : '\032';
ea139302
PB
1049# endif /* TCSIG */
1050#endif /* SIGTSTP */
d0a64c71
GM
1051}
1052
2c9c7136
PB
1053/*
1054 * When we get an AYT, if ^T is enabled, use that. Otherwise,
1055 * just send back "[Yes]".
1056 */
1057recv_ayt()
1058{
1059#if defined(SIGINFO) && defined(TCSIG)
1060 if (slctab[SLC_AYT].sptr && *slctab[SLC_AYT].sptr != _POSIX_VDISABLE) {
1061 (void) ioctl(pty, TCSIG, (char *)SIGINFO);
1062 return;
1063 }
1064#endif
1065 (void) strcpy(nfrontp, "\r\n[Yes]\r\n");
1066 nfrontp += 9;
1067}
1068
ea139302 1069doeof()
d0a64c71 1070{
2c9c7136 1071#if defined(LINEMODE) && defined(USE_TERMIO) && (VEOF == VMIN)
ed8f31c1
PB
1072 extern char oldeofc;
1073#endif
ea139302 1074 init_termbuf();
d0a64c71 1075
2c9c7136 1076#if defined(LINEMODE) && defined(USE_TERMIO) && (VEOF == VMIN)
ed8f31c1
PB
1077 if (!tty_isediting()) {
1078 *pfrontp++ = oldeofc;
1079 return;
1080 }
1081#endif
1082 *pfrontp++ = slctab[SLC_EOF].sptr ?
1083 (unsigned char)*slctab[SLC_EOF].sptr : '\004';
d0a64c71 1084}