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