121182Sdist /*
261451Sbostic * Copyright (c) 1989, 1993
361451Sbostic * The Regents of the University of California. All rights reserved.
433687Sbostic *
542673Sbostic * %sccs.include.redist.c%
621182Sdist */
721182Sdist
86295Sroot #ifndef lint
961451Sbostic static char copyright[] =
1061451Sbostic "@(#) Copyright (c) 1989, 1993\n\
1161451Sbostic The Regents of the University of California. All rights reserved.\n";
1233687Sbostic #endif /* not lint */
136295Sroot
1421182Sdist #ifndef lint
15*69786Sdab static char sccsid[] = "@(#)telnetd.c 8.4 (Berkeley) 05/30/95";
1633687Sbostic #endif /* not lint */
1721182Sdist
1838904Sborman #include "telnetd.h"
1945234Sborman #include "pathnames.h"
2038904Sborman
2160151Sdab #if defined(_SC_CRAY_SECURE_SYS) && !defined(SCM_SECURITY)
2260151Sdab /*
2360151Sdab * UNICOS 6.0/6.1 do not have SCM_SECURITY defined, so we can
2460151Sdab * use it to tell us to turn off all the socket security code,
2560151Sdab * since that is only used in UNICOS 7.0 and later.
2660151Sdab */
2760151Sdab # undef _SC_CRAY_SECURE_SYS
2860151Sdab #endif
2960151Sdab
3057212Sdab #if defined(_SC_CRAY_SECURE_SYS)
3157212Sdab #include <sys/sysv.h>
3257212Sdab #include <sys/secdev.h>
3360151Sdab # ifdef SO_SEC_MULTI /* 8.0 code */
3460151Sdab #include <sys/secparm.h>
3560151Sdab #include <sys/usrv.h>
3660151Sdab # endif /* SO_SEC_MULTI */
3757212Sdab int secflag;
3857212Sdab char tty_dev[16];
3957212Sdab struct secdev dv;
4057212Sdab struct sysv sysv;
4160151Sdab # ifdef SO_SEC_MULTI /* 8.0 code */
4260151Sdab struct socksec ss;
4360151Sdab # else /* SO_SEC_MULTI */ /* 7.0 code */
4457212Sdab struct socket_security ss;
4560151Sdab # endif /* SO_SEC_MULTI */
4657212Sdab #endif /* _SC_CRAY_SECURE_SYS */
4757212Sdab
4857212Sdab #if defined(AUTHENTICATION)
4946809Sdab #include <libtelnet/auth.h>
5046809Sdab int auth_level = 0;
5146809Sdab #endif
5246809Sdab #if defined(SecurID)
5346809Sdab int require_SecurID = 0;
5446809Sdab #endif
5546809Sdab
5657212Sdab extern int utmp_len;
5757212Sdab int registerd_host_only = 0;
5857212Sdab
5957212Sdab #ifdef STREAMSPTY
6057212Sdab # include <stropts.h>
6157212Sdab # include <termio.h>
6257212Sdab /* make sure we don't get the bsd version */
6357212Sdab # include "/usr/include/sys/tty.h"
6457212Sdab # include <sys/ptyvar.h>
6557212Sdab
666002Sroot /*
6757212Sdab * Because of the way ptyibuf is used with streams messages, we need
6857212Sdab * ptyibuf+1 to be on a full-word boundary. The following wierdness
6957212Sdab * is simply to make that happen.
7057212Sdab */
7165158Sdab long ptyibufbuf[BUFSIZ/sizeof(long)+1];
7265158Sdab char *ptyibuf = ((char *)&ptyibufbuf[1])-1;
7365158Sdab char *ptyip = ((char *)&ptyibufbuf[1])-1;
7457212Sdab char ptyibuf2[BUFSIZ];
7557212Sdab unsigned char ctlbuf[BUFSIZ];
7657212Sdab struct strbuf strbufc, strbufd;
7757212Sdab
7857212Sdab int readstream();
7957212Sdab
8057212Sdab #else /* ! STREAMPTY */
8157212Sdab
8257212Sdab /*
8338904Sborman * I/O data buffers,
8438904Sborman * pointers, and counters.
856002Sroot */
8638904Sborman char ptyibuf[BUFSIZ], *ptyip = ptyibuf;
8738904Sborman char ptyibuf2[BUFSIZ];
889218Ssam
8957212Sdab #endif /* ! STREAMPTY */
9057212Sdab
9138904Sborman int hostinfo = 1; /* do we print login banner? */
929218Ssam
9338904Sborman #ifdef CRAY
9438904Sborman extern int newmap; /* nonzero if \n maps to ^M^J */
9540242Sborman int lowpty = 0, highpty; /* low, high pty numbers */
9638904Sborman #endif /* CRAY */
9712216Ssam
9838904Sborman int debug = 0;
9946809Sdab int keepalive = 1;
10038904Sborman char *progname;
1019218Ssam
10246809Sdab extern void usage P((void));
10344363Sborman
10460151Sdab /*
10560151Sdab * The string to pass to getopt(). We do it this way so
10660151Sdab * that only the actual options that we support will be
10760151Sdab * passed off to getopt().
10860151Sdab */
10960151Sdab char valid_opts[] = {
11060151Sdab 'd', ':', 'h', 'k', 'n', 'S', ':', 'u', ':', 'U',
11160151Sdab #ifdef AUTHENTICATION
11260151Sdab 'a', ':', 'X', ':',
11360151Sdab #endif
11460151Sdab #ifdef BFTPDAEMON
11560151Sdab 'B',
11660151Sdab #endif
11760151Sdab #ifdef DIAGNOSTICS
11860151Sdab 'D', ':',
11960151Sdab #endif
12060151Sdab #ifdef ENCRYPTION
12160151Sdab 'e', ':',
12260151Sdab #endif
12360151Sdab #if defined(CRAY) && defined(NEWINIT)
12460151Sdab 'I', ':',
12560151Sdab #endif
12660151Sdab #ifdef LINEMODE
12760151Sdab 'l',
12860151Sdab #endif
12960151Sdab #ifdef CRAY
13060151Sdab 'r', ':',
13160151Sdab #endif
13260151Sdab #ifdef SecurID
13360151Sdab 's',
13460151Sdab #endif
13560151Sdab '\0'
13660151Sdab };
13760151Sdab
main(argc,argv)13838904Sborman main(argc, argv)
13938904Sborman char *argv[];
14038904Sborman {
14138904Sborman struct sockaddr_in from;
14238904Sborman int on = 1, fromlen;
14346809Sdab register int ch;
14446809Sdab extern char *optarg;
14546809Sdab extern int optind;
14646809Sdab #if defined(IPPROTO_IP) && defined(IP_TOS)
14746809Sdab int tos = -1;
14846809Sdab #endif
1496002Sroot
15038904Sborman pfrontp = pbackp = ptyobuf;
15138904Sborman netip = netibuf;
15238904Sborman nfrontp = nbackp = netobuf;
15360151Sdab #ifdef ENCRYPTION
15446809Sdab nclearto = 0;
15560151Sdab #endif /* ENCRYPTION */
1566002Sroot
15738904Sborman progname = *argv;
15840242Sborman
15940242Sborman #ifdef CRAY
16040242Sborman /*
16140242Sborman * Get number of pty's before trying to process options,
16240242Sborman * which may include changing pty range.
16340242Sborman */
16440242Sborman highpty = getnpty();
16540242Sborman #endif /* CRAY */
16640242Sborman
16760151Sdab while ((ch = getopt(argc, argv, valid_opts)) != EOF) {
16846809Sdab switch(ch) {
16927649Sminshall
17057212Sdab #ifdef AUTHENTICATION
17146809Sdab case 'a':
17246809Sdab /*
17346809Sdab * Check for required authentication level
17446809Sdab */
17546809Sdab if (strcmp(optarg, "debug") == 0) {
17646809Sdab extern int auth_debug_mode;
17746809Sdab auth_debug_mode = 1;
17846809Sdab } else if (strcasecmp(optarg, "none") == 0) {
17946809Sdab auth_level = 0;
18046809Sdab } else if (strcasecmp(optarg, "other") == 0) {
18146809Sdab auth_level = AUTH_OTHER;
18246809Sdab } else if (strcasecmp(optarg, "user") == 0) {
18346809Sdab auth_level = AUTH_USER;
18446809Sdab } else if (strcasecmp(optarg, "valid") == 0) {
18546809Sdab auth_level = AUTH_VALID;
18646809Sdab } else if (strcasecmp(optarg, "off") == 0) {
18746809Sdab /*
18846809Sdab * This hack turns off authentication
18946809Sdab */
19046809Sdab auth_level = -1;
19146809Sdab } else {
19246809Sdab fprintf(stderr,
19346809Sdab "telnetd: unknown authorization level for -a\n");
19446809Sdab }
19546809Sdab break;
19657212Sdab #endif /* AUTHENTICATION */
19727649Sminshall
19846809Sdab #ifdef BFTPDAEMON
19946809Sdab case 'B':
20046809Sdab bftpd++;
20146809Sdab break;
20246809Sdab #endif /* BFTPDAEMON */
20346809Sdab
20446809Sdab case 'd':
20546809Sdab if (strcmp(optarg, "ebug") == 0) {
20646809Sdab debug++;
20746809Sdab break;
20846809Sdab }
20946809Sdab usage();
21046809Sdab /* NOTREACHED */
21146809Sdab break;
21246809Sdab
21346809Sdab #ifdef DIAGNOSTICS
21446809Sdab case 'D':
21546809Sdab /*
21646809Sdab * Check for desired diagnostics capabilities.
21746809Sdab */
21846809Sdab if (!strcmp(optarg, "report")) {
21946809Sdab diagnostic |= TD_REPORT|TD_OPTIONS;
22046809Sdab } else if (!strcmp(optarg, "exercise")) {
22146809Sdab diagnostic |= TD_EXERCISE;
22246809Sdab } else if (!strcmp(optarg, "netdata")) {
22346809Sdab diagnostic |= TD_NETDATA;
22446809Sdab } else if (!strcmp(optarg, "ptydata")) {
22546809Sdab diagnostic |= TD_PTYDATA;
22646809Sdab } else if (!strcmp(optarg, "options")) {
22746809Sdab diagnostic |= TD_OPTIONS;
22846809Sdab } else {
22946809Sdab usage();
23046809Sdab /* NOT REACHED */
23146809Sdab }
23246809Sdab break;
23346809Sdab #endif /* DIAGNOSTICS */
23446809Sdab
23557212Sdab #ifdef ENCRYPTION
23646809Sdab case 'e':
23746809Sdab if (strcmp(optarg, "debug") == 0) {
23846809Sdab extern int encrypt_debug_mode;
23946809Sdab encrypt_debug_mode = 1;
24046809Sdab break;
24146809Sdab }
24246809Sdab usage();
24346809Sdab /* NOTREACHED */
24446809Sdab break;
24557212Sdab #endif /* ENCRYPTION */
24646809Sdab
24746809Sdab case 'h':
24846809Sdab hostinfo = 0;
24946809Sdab break;
25046809Sdab
25146809Sdab #if defined(CRAY) && defined(NEWINIT)
25246809Sdab case 'I':
25346809Sdab {
25446809Sdab extern char *gen_id;
25546809Sdab gen_id = optarg;
25646809Sdab break;
25746809Sdab }
25846809Sdab #endif /* defined(CRAY) && defined(NEWINIT) */
25946809Sdab
26038904Sborman #ifdef LINEMODE
26146809Sdab case 'l':
26246809Sdab alwayslinemode = 1;
26346809Sdab break;
26438904Sborman #endif /* LINEMODE */
26527649Sminshall
26657212Sdab case 'k':
26757212Sdab #if defined(LINEMODE) && defined(KLUDGELINEMODE)
26857212Sdab lmodetype = NO_AUTOKLUDGE;
26957212Sdab #else
27057212Sdab /* ignore -k option if built without kludge linemode */
27157212Sdab #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */
27257212Sdab break;
27357212Sdab
27446809Sdab case 'n':
27546809Sdab keepalive = 0;
27646809Sdab break;
27727649Sminshall
27845234Sborman #ifdef CRAY
27946809Sdab case 'r':
28046809Sdab {
28146809Sdab char *strchr();
28246809Sdab char *c;
28327649Sminshall
28446809Sdab /*
28546809Sdab * Allow the specification of alterations
28646809Sdab * to the pty search range. It is legal to
28746809Sdab * specify only one, and not change the
28846809Sdab * other from its default.
28946809Sdab */
29046809Sdab c = strchr(optarg, '-');
29146809Sdab if (c) {
29246809Sdab *c++ = '\0';
29346809Sdab highpty = atoi(c);
29444363Sborman }
29546809Sdab if (*optarg != '\0')
29646809Sdab lowpty = atoi(optarg);
29746809Sdab if ((lowpty > highpty) || (lowpty < 0) ||
29846809Sdab (highpty > 32767)) {
29944363Sborman usage();
30044363Sborman /* NOT REACHED */
30144363Sborman }
30246809Sdab break;
30346809Sdab }
30438904Sborman #endif /* CRAY */
3056002Sroot
30646809Sdab #ifdef SecurID
30746809Sdab case 's':
30846809Sdab /* SecurID required */
30946809Sdab require_SecurID = 1;
31046809Sdab break;
31146809Sdab #endif /* SecurID */
31246809Sdab case 'S':
31346809Sdab #ifdef HAS_GETTOS
31446809Sdab if ((tos = parsetos(optarg, "tcp")) < 0)
31546809Sdab fprintf(stderr, "%s%s%s\n",
31646809Sdab "telnetd: Bad TOS argument '", optarg,
31746809Sdab "'; will try to use default TOS");
31846809Sdab #else
31946809Sdab fprintf(stderr, "%s%s\n", "TOS option unavailable; ",
32046809Sdab "-S flag not supported\n");
32146809Sdab #endif
32246809Sdab break;
32346809Sdab
32457212Sdab case 'u':
32557212Sdab utmp_len = atoi(optarg);
32657212Sdab break;
32757212Sdab
32857212Sdab case 'U':
32957212Sdab registerd_host_only = 1;
33057212Sdab break;
33157212Sdab
33257212Sdab #ifdef AUTHENTICATION
33346809Sdab case 'X':
33446809Sdab /*
33546809Sdab * Check for invalid authentication types
33646809Sdab */
33746809Sdab auth_disable_name(optarg);
33846809Sdab break;
33957212Sdab #endif /* AUTHENTICATION */
34046809Sdab
34146809Sdab default:
34260151Sdab fprintf(stderr, "telnetd: %c: unknown option\n", ch);
34346809Sdab /* FALLTHROUGH */
34446809Sdab case '?':
34544363Sborman usage();
34646809Sdab /* NOTREACHED */
34744363Sborman }
34844363Sborman }
34944363Sborman
35046809Sdab argc -= optind;
35146809Sdab argv += optind;
35244363Sborman
35338904Sborman if (debug) {
35427185Sminshall int s, ns, foo;
35527185Sminshall struct servent *sp;
35627185Sminshall static struct sockaddr_in sin = { AF_INET };
35727185Sminshall
35844363Sborman if (argc > 1) {
35944363Sborman usage();
36044363Sborman /* NOT REACHED */
36144363Sborman } else if (argc == 1) {
36238904Sborman if (sp = getservbyname(*argv, "tcp")) {
36338904Sborman sin.sin_port = sp->s_port;
36438904Sborman } else {
36538904Sborman sin.sin_port = atoi(*argv);
36638904Sborman if ((int)sin.sin_port <= 0) {
36738904Sborman fprintf(stderr, "telnetd: %s: bad port #\n", *argv);
36844363Sborman usage();
36944363Sborman /* NOT REACHED */
37038904Sborman }
37138904Sborman sin.sin_port = htons((u_short)sin.sin_port);
37238904Sborman }
37337210Sminshall } else {
37437210Sminshall sp = getservbyname("telnet", "tcp");
37537210Sminshall if (sp == 0) {
37644363Sborman fprintf(stderr, "telnetd: tcp/telnet: unknown service\n");
37738904Sborman exit(1);
37837210Sminshall }
37937210Sminshall sin.sin_port = sp->s_port;
38027185Sminshall }
38127185Sminshall
38227185Sminshall s = socket(AF_INET, SOCK_STREAM, 0);
38327185Sminshall if (s < 0) {
38427185Sminshall perror("telnetd: socket");;
38527185Sminshall exit(1);
38627185Sminshall }
38757212Sdab (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
38857212Sdab (char *)&on, sizeof(on));
38938904Sborman if (bind(s, (struct sockaddr *)&sin, sizeof sin) < 0) {
39027185Sminshall perror("bind");
39127185Sminshall exit(1);
39227185Sminshall }
39327185Sminshall if (listen(s, 1) < 0) {
39427185Sminshall perror("listen");
39527185Sminshall exit(1);
39627185Sminshall }
39727185Sminshall foo = sizeof sin;
39838904Sborman ns = accept(s, (struct sockaddr *)&sin, &foo);
39927185Sminshall if (ns < 0) {
40027185Sminshall perror("accept");
40127185Sminshall exit(1);
40227185Sminshall }
40338904Sborman (void) dup2(ns, 0);
40438904Sborman (void) close(ns);
40538904Sborman (void) close(s);
40646809Sdab #ifdef convex
40746809Sdab } else if (argc == 1) {
40846809Sdab ; /* VOID*/ /* Just ignore the host/port name */
40946809Sdab #endif
41044363Sborman } else if (argc > 0) {
41144363Sborman usage();
41244363Sborman /* NOT REACHED */
41327185Sminshall }
41438904Sborman
41557212Sdab #if defined(_SC_CRAY_SECURE_SYS)
41657212Sdab secflag = sysconf(_SC_CRAY_SECURE_SYS);
41757212Sdab
41857212Sdab /*
41960151Sdab * Get socket's security label
42057212Sdab */
42157212Sdab if (secflag) {
42260151Sdab int szss = sizeof(ss);
42360151Sdab #ifdef SO_SEC_MULTI /* 8.0 code */
42460151Sdab int sock_multi;
42560151Sdab int szi = sizeof(int);
42660151Sdab #endif /* SO_SEC_MULTI */
42757212Sdab
428*69786Sdab memset((char *)&dv, 0, sizeof(dv));
42957212Sdab
43057212Sdab if (getsysv(&sysv, sizeof(struct sysv)) != 0) {
43157212Sdab perror("getsysv");
43257212Sdab exit(1);
43357212Sdab }
43457212Sdab
43557212Sdab /*
43660151Sdab * Get socket security label and set device values
43760151Sdab * {security label to be set on ttyp device}
43857212Sdab */
43960151Sdab #ifdef SO_SEC_MULTI /* 8.0 code */
44060151Sdab if ((getsockopt(0, SOL_SOCKET, SO_SECURITY,
44160151Sdab (char *)&ss, &szss) < 0) ||
44260151Sdab (getsockopt(0, SOL_SOCKET, SO_SEC_MULTI,
44360151Sdab (char *)&sock_multi, &szi) < 0)) {
44460151Sdab perror("getsockopt");
44560151Sdab exit(1);
44660151Sdab } else {
44760151Sdab dv.dv_actlvl = ss.ss_actlabel.lt_level;
44860151Sdab dv.dv_actcmp = ss.ss_actlabel.lt_compart;
44960151Sdab if (!sock_multi) {
45060151Sdab dv.dv_minlvl = dv.dv_maxlvl = dv.dv_actlvl;
45160151Sdab dv.dv_valcmp = dv.dv_actcmp;
45260151Sdab } else {
45360151Sdab dv.dv_minlvl = ss.ss_minlabel.lt_level;
45460151Sdab dv.dv_maxlvl = ss.ss_maxlabel.lt_level;
45560151Sdab dv.dv_valcmp = ss.ss_maxlabel.lt_compart;
45660151Sdab }
45760151Sdab dv.dv_devflg = 0;
45860151Sdab }
45960151Sdab #else /* SO_SEC_MULTI */ /* 7.0 code */
46057212Sdab if (getsockopt(0, SOL_SOCKET, SO_SECURITY,
46160151Sdab (char *)&ss, &szss) >= 0) {
46257212Sdab dv.dv_actlvl = ss.ss_slevel;
46357212Sdab dv.dv_actcmp = ss.ss_compart;
46457212Sdab dv.dv_minlvl = ss.ss_minlvl;
46557212Sdab dv.dv_maxlvl = ss.ss_maxlvl;
46657212Sdab dv.dv_valcmp = ss.ss_maxcmp;
46757212Sdab }
46860151Sdab #endif /* SO_SEC_MULTI */
46957212Sdab }
47057212Sdab #endif /* _SC_CRAY_SECURE_SYS */
47157212Sdab
47224855Seric openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON);
47316371Skarels fromlen = sizeof (from);
47438904Sborman if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) {
47538904Sborman fprintf(stderr, "%s: ", progname);
47616371Skarels perror("getpeername");
47716371Skarels _exit(1);
4788346Ssam }
47946809Sdab if (keepalive &&
48057212Sdab setsockopt(0, SOL_SOCKET, SO_KEEPALIVE,
48157212Sdab (char *)&on, sizeof (on)) < 0) {
48217187Sralph syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
48310418Ssam }
48440242Sborman
48546809Sdab #if defined(IPPROTO_IP) && defined(IP_TOS)
48646809Sdab {
48746809Sdab # if defined(HAS_GETTOS)
48846809Sdab struct tosent *tp;
48946809Sdab if (tos < 0 && (tp = gettosbyname("telnet", "tcp")))
49046809Sdab tos = tp->t_tos;
49146809Sdab # endif
49246809Sdab if (tos < 0)
49346809Sdab tos = 020; /* Low Delay bit */
49446809Sdab if (tos
49557212Sdab && (setsockopt(0, IPPROTO_IP, IP_TOS,
49657212Sdab (char *)&tos, sizeof(tos)) < 0)
49746809Sdab && (errno != ENOPROTOOPT) )
49846809Sdab syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
49946809Sdab }
50046809Sdab #endif /* defined(IPPROTO_IP) && defined(IP_TOS) */
50138904Sborman net = 0;
50238904Sborman doit(&from);
50338904Sborman /* NOTREACHED */
50438904Sborman } /* end of main */
5056002Sroot
50646809Sdab void
usage()50744363Sborman usage()
50844363Sborman {
50946809Sdab fprintf(stderr, "Usage: telnetd");
51057212Sdab #ifdef AUTHENTICATION
51160151Sdab fprintf(stderr, " [-a (debug|other|user|valid|off|none)]\n\t");
51246809Sdab #endif
51346809Sdab #ifdef BFTPDAEMON
51446809Sdab fprintf(stderr, " [-B]");
51546809Sdab #endif
51646809Sdab fprintf(stderr, " [-debug]");
51746809Sdab #ifdef DIAGNOSTICS
51846809Sdab fprintf(stderr, " [-D (options|report|exercise|netdata|ptydata)]\n\t");
51946809Sdab #endif
52057212Sdab #ifdef AUTHENTICATION
52146809Sdab fprintf(stderr, " [-edebug]");
52246809Sdab #endif
52346809Sdab fprintf(stderr, " [-h]");
52446809Sdab #if defined(CRAY) && defined(NEWINIT)
52544363Sborman fprintf(stderr, " [-Iinitid]");
52646809Sdab #endif
52760151Sdab #if defined(LINEMODE) && defined(KLUDGELINEMODE)
52860151Sdab fprintf(stderr, " [-k]");
52960151Sdab #endif
53044363Sborman #ifdef LINEMODE
53144363Sborman fprintf(stderr, " [-l]");
53244363Sborman #endif
53346809Sdab fprintf(stderr, " [-n]");
53444363Sborman #ifdef CRAY
53544363Sborman fprintf(stderr, " [-r[lowpty]-[highpty]]");
53644363Sborman #endif
53760151Sdab fprintf(stderr, "\n\t");
53846809Sdab #ifdef SecurID
53946809Sdab fprintf(stderr, " [-s]");
54046809Sdab #endif
54160151Sdab #ifdef HAS_GETTOS
54260151Sdab fprintf(stderr, " [-S tos]");
54360151Sdab #endif
54457212Sdab #ifdef AUTHENTICATION
54546809Sdab fprintf(stderr, " [-X auth-type]");
54646809Sdab #endif
54757212Sdab fprintf(stderr, " [-u utmp_hostname_length] [-U]");
54844363Sborman fprintf(stderr, " [port]\n");
54944363Sborman exit(1);
55044363Sborman }
55144363Sborman
55227649Sminshall /*
55327983Sminshall * getterminaltype
55427649Sminshall *
55538904Sborman * Ask the other end to send along its terminal type and speed.
55627983Sminshall * Output is the variable terminaltype filled in.
55727649Sminshall */
55865158Sdab static unsigned char ttytype_sbbuf[] = {
55965158Sdab IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE
56065158Sdab };
56146809Sdab
56246809Sdab int
getterminaltype(name)56346809Sdab getterminaltype(name)
56446809Sdab char *name;
56527649Sminshall {
56646809Sdab int retval = -1;
56746809Sdab void _gettermname();
56827649Sminshall
56938904Sborman settimer(baseline);
57057212Sdab #if defined(AUTHENTICATION)
57146809Sdab /*
57246809Sdab * Handle the Authentication option before we do anything else.
57346809Sdab */
57446809Sdab send_do(TELOPT_AUTHENTICATION, 1);
57546809Sdab while (his_will_wont_is_changing(TELOPT_AUTHENTICATION))
57646809Sdab ttloop();
57746809Sdab if (his_state_is_will(TELOPT_AUTHENTICATION)) {
57846809Sdab retval = auth_wait(name);
57946809Sdab }
58046809Sdab #endif
58146809Sdab
58260151Sdab #ifdef ENCRYPTION
58346809Sdab send_will(TELOPT_ENCRYPT, 1);
58460151Sdab #endif /* ENCRYPTION */
58539503Sborman send_do(TELOPT_TTYPE, 1);
58639503Sborman send_do(TELOPT_TSPEED, 1);
58744363Sborman send_do(TELOPT_XDISPLOC, 1);
58865158Sdab send_do(TELOPT_NEW_ENVIRON, 1);
58965158Sdab send_do(TELOPT_OLD_ENVIRON, 1);
59046809Sdab while (
59160151Sdab #ifdef ENCRYPTION
59246809Sdab his_do_dont_is_changing(TELOPT_ENCRYPT) ||
59360151Sdab #endif /* ENCRYPTION */
59446809Sdab his_will_wont_is_changing(TELOPT_TTYPE) ||
59544363Sborman his_will_wont_is_changing(TELOPT_TSPEED) ||
59644363Sborman his_will_wont_is_changing(TELOPT_XDISPLOC) ||
59765158Sdab his_will_wont_is_changing(TELOPT_NEW_ENVIRON) ||
59865158Sdab his_will_wont_is_changing(TELOPT_OLD_ENVIRON)) {
59927983Sminshall ttloop();
60027649Sminshall }
60160151Sdab #ifdef ENCRYPTION
60246809Sdab /*
60346809Sdab * Wait for the negotiation of what type of encryption we can
60446809Sdab * send with. If autoencrypt is not set, this will just return.
60546809Sdab */
60646809Sdab if (his_state_is_will(TELOPT_ENCRYPT)) {
60746809Sdab encrypt_wait();
60846809Sdab }
60960151Sdab #endif /* ENCRYPTION */
61044363Sborman if (his_state_is_will(TELOPT_TSPEED)) {
61165158Sdab static unsigned char sb[] =
61265158Sdab { IAC, SB, TELOPT_TSPEED, TELQUAL_SEND, IAC, SE };
61327983Sminshall
614*69786Sdab memmove(nfrontp, sb, sizeof sb);
61565158Sdab nfrontp += sizeof sb;
616*69786Sdab DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2););
61738904Sborman }
61844363Sborman if (his_state_is_will(TELOPT_XDISPLOC)) {
61965158Sdab static unsigned char sb[] =
62065158Sdab { IAC, SB, TELOPT_XDISPLOC, TELQUAL_SEND, IAC, SE };
62138904Sborman
622*69786Sdab memmove(nfrontp, sb, sizeof sb);
62365158Sdab nfrontp += sizeof sb;
624*69786Sdab DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2););
62544363Sborman }
62665158Sdab if (his_state_is_will(TELOPT_NEW_ENVIRON)) {
62765158Sdab static unsigned char sb[] =
62865158Sdab { IAC, SB, TELOPT_NEW_ENVIRON, TELQUAL_SEND, IAC, SE };
62944363Sborman
630*69786Sdab memmove(nfrontp, sb, sizeof sb);
63165158Sdab nfrontp += sizeof sb;
632*69786Sdab DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2););
63344363Sborman }
63465158Sdab else if (his_state_is_will(TELOPT_OLD_ENVIRON)) {
63565158Sdab static unsigned char sb[] =
63665158Sdab { IAC, SB, TELOPT_OLD_ENVIRON, TELQUAL_SEND, IAC, SE };
63765158Sdab
638*69786Sdab memmove(nfrontp, sb, sizeof sb);
63965158Sdab nfrontp += sizeof sb;
640*69786Sdab DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2););
64165158Sdab }
64244363Sborman if (his_state_is_will(TELOPT_TTYPE)) {
64344363Sborman
644*69786Sdab memmove(nfrontp, ttytype_sbbuf, sizeof ttytype_sbbuf);
64538904Sborman nfrontp += sizeof ttytype_sbbuf;
646*69786Sdab DIAG(TD_OPTIONS, printsub('>', ttytype_sbbuf + 2,
647*69786Sdab sizeof ttytype_sbbuf - 2););
64838904Sborman }
64944363Sborman if (his_state_is_will(TELOPT_TSPEED)) {
65038904Sborman while (sequenceIs(tspeedsubopt, baseline))
65127983Sminshall ttloop();
65238904Sborman }
65344363Sborman if (his_state_is_will(TELOPT_XDISPLOC)) {
65444363Sborman while (sequenceIs(xdisplocsubopt, baseline))
65544363Sborman ttloop();
65644363Sborman }
65765158Sdab if (his_state_is_will(TELOPT_NEW_ENVIRON)) {
65844363Sborman while (sequenceIs(environsubopt, baseline))
65944363Sborman ttloop();
66044363Sborman }
66165158Sdab if (his_state_is_will(TELOPT_OLD_ENVIRON)) {
66265158Sdab while (sequenceIs(oenvironsubopt, baseline))
66365158Sdab ttloop();
66465158Sdab }
66544363Sborman if (his_state_is_will(TELOPT_TTYPE)) {
66638904Sborman char first[256], last[256];
66738904Sborman
66838904Sborman while (sequenceIs(ttypesubopt, baseline))
66938904Sborman ttloop();
67038904Sborman
67144363Sborman /*
67244363Sborman * If the other side has already disabled the option, then
67344363Sborman * we have to just go with what we (might) have already gotten.
67444363Sborman */
67544363Sborman if (his_state_is_will(TELOPT_TTYPE) && !terminaltypeok(terminaltype)) {
67638904Sborman (void) strncpy(first, terminaltype, sizeof(first));
67738904Sborman for(;;) {
67838904Sborman /*
67938904Sborman * Save the unknown name, and request the next name.
68038904Sborman */
68138904Sborman (void) strncpy(last, terminaltype, sizeof(last));
68238904Sborman _gettermname();
68344363Sborman if (terminaltypeok(terminaltype))
68438904Sborman break;
68544363Sborman if ((strncmp(last, terminaltype, sizeof(last)) == 0) ||
68644363Sborman his_state_is_wont(TELOPT_TTYPE)) {
68738904Sborman /*
68838904Sborman * We've hit the end. If this is the same as
68938904Sborman * the first name, just go with it.
69038904Sborman */
69145234Sborman if (strncmp(first, terminaltype, sizeof(first)) == 0)
69238904Sborman break;
69338904Sborman /*
69444363Sborman * Get the terminal name one more time, so that
69538904Sborman * RFC1091 compliant telnets will cycle back to
69638904Sborman * the start of the list.
69738904Sborman */
69844363Sborman _gettermname();
69945234Sborman if (strncmp(first, terminaltype, sizeof(first)) != 0)
70038904Sborman (void) strncpy(terminaltype, first, sizeof(first));
70138904Sborman break;
70238904Sborman }
70338904Sborman }
70427983Sminshall }
70527983Sminshall }
70646809Sdab return(retval);
70738904Sborman } /* end of getterminaltype */
70838904Sborman
70946809Sdab void
_gettermname()71038904Sborman _gettermname()
71138904Sborman {
71244363Sborman /*
71344363Sborman * If the client turned off the option,
71444363Sborman * we can't send another request, so we
71544363Sborman * just return.
71644363Sborman */
71744363Sborman if (his_state_is_wont(TELOPT_TTYPE))
71844363Sborman return;
71938904Sborman settimer(baseline);
720*69786Sdab memmove(nfrontp, ttytype_sbbuf, sizeof ttytype_sbbuf);
72138904Sborman nfrontp += sizeof ttytype_sbbuf;
722*69786Sdab DIAG(TD_OPTIONS, printsub('>', ttytype_sbbuf + 2,
723*69786Sdab sizeof ttytype_sbbuf - 2););
72438904Sborman while (sequenceIs(ttypesubopt, baseline))
72538904Sborman ttloop();
72627649Sminshall }
72727649Sminshall
72846809Sdab int
terminaltypeok(s)72938904Sborman terminaltypeok(s)
73046809Sdab char *s;
73138904Sborman {
73238904Sborman char buf[1024];
73338904Sborman
73438904Sborman if (terminaltype == NULL)
73538904Sborman return(1);
73638904Sborman
73738904Sborman /*
73838904Sborman * tgetent() will return 1 if the type is known, and
73938904Sborman * 0 if it is not known. If it returns -1, it couldn't
74038904Sborman * open the database. But if we can't open the database,
74138904Sborman * it won't help to say we failed, because we won't be
74238904Sborman * able to verify anything else. So, we treat -1 like 1.
74338904Sborman */
74438904Sborman if (tgetent(buf, s) == 0)
74538904Sborman return(0);
74638904Sborman return(1);
74738904Sborman }
74838904Sborman
74946809Sdab #ifndef MAXHOSTNAMELEN
75046809Sdab #define MAXHOSTNAMELEN 64
75146809Sdab #endif /* MAXHOSTNAMELEN */
75246809Sdab
75346809Sdab char *hostname;
75446809Sdab char host_name[MAXHOSTNAMELEN];
75546809Sdab char remote_host_name[MAXHOSTNAMELEN];
75646809Sdab
75746809Sdab #ifndef convex
75846809Sdab extern void telnet P((int, int));
75946809Sdab #else
76046809Sdab extern void telnet P((int, int, char *));
76146809Sdab #endif
76246809Sdab
7636002Sroot /*
7646002Sroot * Get a pty, scan input lines.
7656002Sroot */
76638904Sborman doit(who)
76712683Ssam struct sockaddr_in *who;
7686002Sroot {
76920188Skarels char *host, *inet_ntoa();
77038904Sborman int t;
77112683Ssam struct hostent *hp;
77246809Sdab int level;
77360151Sdab int ptynum;
77446809Sdab char user_name[256];
7756002Sroot
77638904Sborman /*
77738904Sborman * Find an available pty to use.
77838904Sborman */
77945234Sborman #ifndef convex
78060151Sdab pty = getpty(&ptynum);
78138904Sborman if (pty < 0)
78238904Sborman fatal(net, "All network ports in use");
78345234Sborman #else
78445234Sborman for (;;) {
78545234Sborman char *lp;
78645234Sborman extern char *line, *getpty();
78720188Skarels
78845234Sborman if ((lp = getpty()) == NULL)
78945234Sborman fatal(net, "Out of ptys");
79045234Sborman
79145234Sborman if ((pty = open(lp, 2)) >= 0) {
79245234Sborman strcpy(line,lp);
79345234Sborman line[5] = 't';
79445234Sborman break;
79545234Sborman }
79645234Sborman }
79744545Smarc #endif
79838904Sborman
79957212Sdab #if defined(_SC_CRAY_SECURE_SYS)
80057212Sdab /*
801*69786Sdab * set ttyp line security label
80257212Sdab */
80357212Sdab if (secflag) {
80460151Sdab char slave_dev[16];
80560151Sdab
80660151Sdab sprintf(tty_dev, "/dev/pty/%03d", ptynum);
80760151Sdab if (setdevs(tty_dev, &dv) < 0)
80860151Sdab fatal(net, "cannot set pty security");
80960151Sdab sprintf(slave_dev, "/dev/ttyp%03d", ptynum);
81060151Sdab if (setdevs(slave_dev, &dv) < 0)
81160151Sdab fatal(net, "cannot set tty security");
81257212Sdab }
81357212Sdab #endif /* _SC_CRAY_SECURE_SYS */
81457212Sdab
81538904Sborman /* get name of connected client */
81638904Sborman hp = gethostbyaddr((char *)&who->sin_addr, sizeof (struct in_addr),
81712683Ssam who->sin_family);
81857212Sdab
81957641Sdab if (hp == NULL && registerd_host_only) {
82057641Sdab fatal(net, "Couldn't resolve your address into a host name.\r\n\
821*69786Sdab Please contact your net administrator");
82257641Sdab } else if (hp &&
823*69786Sdab (strlen(hp->h_name) <= (unsigned int)((utmp_len < 0) ? -utmp_len
824*69786Sdab : utmp_len))) {
82512683Ssam host = hp->h_name;
82657212Sdab } else {
82717444Sralph host = inet_ntoa(who->sin_addr);
82857212Sdab }
82946809Sdab /*
83046809Sdab * We must make a copy because Kerberos is probably going
83146809Sdab * to also do a gethost* and overwrite the static data...
83246809Sdab */
83346809Sdab strncpy(remote_host_name, host, sizeof(remote_host_name)-1);
83446809Sdab remote_host_name[sizeof(remote_host_name)-1] = 0;
83546809Sdab host = remote_host_name;
83627983Sminshall
83746809Sdab (void) gethostname(host_name, sizeof (host_name));
83846809Sdab hostname = host_name;
83946809Sdab
84057212Sdab #if defined(AUTHENTICATION) || defined(ENCRYPTION)
84146809Sdab auth_encrypt_init(hostname, host, "TELNETD", 1);
84246809Sdab #endif
84346809Sdab
84444363Sborman init_env();
84527983Sminshall /*
84638904Sborman * get terminal type.
84727983Sminshall */
84846809Sdab *user_name = 0;
84946809Sdab level = getterminaltype(user_name);
85044363Sborman setenv("TERM", terminaltype ? terminaltype : "network", 1);
85127983Sminshall
85227649Sminshall /*
85338904Sborman * Start up the login process on the slave side of the terminal
85427649Sminshall */
85545234Sborman #ifndef convex
85646809Sdab startslave(host, level, user_name);
85738904Sborman
85860151Sdab #if defined(_SC_CRAY_SECURE_SYS)
85960151Sdab if (secflag) {
86060151Sdab if (setulvl(dv.dv_actlvl) < 0)
86160151Sdab fatal(net,"cannot setulvl()");
86260151Sdab if (setucmp(dv.dv_actcmp) < 0)
86360151Sdab fatal(net, "cannot setucmp()");
86460151Sdab }
86560151Sdab #endif /* _SC_CRAY_SECURE_SYS */
86660151Sdab
86738904Sborman telnet(net, pty); /* begin server processing */
86845234Sborman #else
86945234Sborman telnet(net, pty, host);
87045234Sborman #endif
8719244Ssam /*NOTREACHED*/
87238904Sborman } /* end of doit */
8739244Ssam
87446809Sdab #if defined(CRAY2) && defined(UNICOS5) && defined(UNICOS50)
87546809Sdab int
Xterm_output(ibufp,obuf,icountp,ocount)87646809Sdab Xterm_output(ibufp, obuf, icountp, ocount)
87746809Sdab char **ibufp, *obuf;
87846809Sdab int *icountp, ocount;
87946809Sdab {
88046809Sdab int ret;
88146809Sdab ret = term_output(*ibufp, obuf, *icountp, ocount);
88246809Sdab *ibufp += *icountp;
88346809Sdab *icountp = 0;
88446809Sdab return(ret);
88546809Sdab }
88646809Sdab #define term_output Xterm_output
88746809Sdab #endif /* defined(CRAY2) && defined(UNICOS5) && defined(UNICOS50) */
88846809Sdab
8896002Sroot /*
8906002Sroot * Main loop. Select from pty and network, and
8916002Sroot * hand data to telnet receiver finite state machine.
8926002Sroot */
89346809Sdab void
89445234Sborman #ifndef convex
telnet(f,p)8956002Sroot telnet(f, p)
89645234Sborman #else
89745234Sborman telnet(f, p, host)
89845234Sborman #endif
89946809Sdab int f, p;
90045234Sborman #ifdef convex
90146809Sdab char *host;
90245234Sborman #endif
9036002Sroot {
9046002Sroot int on = 1;
90533271Sminshall #define TABBUFSIZ 512
90633271Sminshall char defent[TABBUFSIZ];
90733271Sminshall char defstrs[TABBUFSIZ];
90833271Sminshall #undef TABBUFSIZ
90933271Sminshall char *HE;
91033271Sminshall char *HN;
91133271Sminshall char *IM;
91238904Sborman void netflush();
91368346Sdab int nfd;
91446809Sdab
91532400Sminshall /*
91638904Sborman * Initialize the slc mapping table.
91732400Sminshall */
91838904Sborman get_slc_defaults();
9196002Sroot
9208379Ssam /*
92138904Sborman * Do some tests where it is desireable to wait for a response.
92238904Sborman * Rather than doing them slowly, one at a time, do them all
92338904Sborman * at once.
9248379Ssam */
92544363Sborman if (my_state_is_wont(TELOPT_SGA))
92639503Sborman send_will(TELOPT_SGA, 1);
92712713Ssam /*
92827649Sminshall * Is the client side a 4.2 (NOT 4.3) system? We need to know this
92927649Sminshall * because 4.2 clients are unable to deal with TCP urgent data.
93027649Sminshall *
93127649Sminshall * To find out, we send out a "DO ECHO". If the remote system
93227649Sminshall * answers "WILL ECHO" it is probably a 4.2 client, and we note
93327649Sminshall * that fact ("WILL ECHO" ==> that the client will echo what
93427649Sminshall * WE, the server, sends it; it does NOT mean that the client will
93527649Sminshall * echo the terminal input).
93627649Sminshall */
93739503Sborman send_do(TELOPT_ECHO, 1);
93827649Sminshall
93938904Sborman #ifdef LINEMODE
94044363Sborman if (his_state_is_wont(TELOPT_LINEMODE)) {
94138904Sborman /* Query the peer for linemode support by trying to negotiate
94238904Sborman * the linemode option.
94338904Sborman */
94444363Sborman linemode = 0;
94538904Sborman editmode = 0;
94639503Sborman send_do(TELOPT_LINEMODE, 1); /* send do linemode */
94738904Sborman }
94838904Sborman #endif /* LINEMODE */
94938904Sborman
95027649Sminshall /*
95138904Sborman * Send along a couple of other options that we wish to negotiate.
95238904Sborman */
95339503Sborman send_do(TELOPT_NAWS, 1);
95439503Sborman send_will(TELOPT_STATUS, 1);
95557597Sdab flowmode = 1; /* default flow control state */
95657597Sdab restartany = -1; /* uninitialized... */
95739503Sborman send_do(TELOPT_LFLOW, 1);
95838904Sborman
95938904Sborman /*
96038904Sborman * Spin, waiting for a response from the DO ECHO. However,
96138904Sborman * some REALLY DUMB telnets out there might not respond
96238904Sborman * to the DO ECHO. So, we spin looking for NAWS, (most dumb
96338904Sborman * telnets so far seem to respond with WONT for a DO that
96438904Sborman * they don't understand...) because by the time we get the
96538904Sborman * response, it will already have processed the DO ECHO.
96638904Sborman * Kludge upon kludge.
96738904Sborman */
96844363Sborman while (his_will_wont_is_changing(TELOPT_NAWS))
96938904Sborman ttloop();
97038904Sborman
97138904Sborman /*
97244363Sborman * But...
97344363Sborman * The client might have sent a WILL NAWS as part of its
97444363Sborman * startup code; if so, we'll be here before we get the
97544363Sborman * response to the DO ECHO. We'll make the assumption
97644363Sborman * that any implementation that understands about NAWS
97744363Sborman * is a modern enough implementation that it will respond
97844363Sborman * to our DO ECHO request; hence we'll do another spin
97944363Sborman * waiting for the ECHO option to settle down, which is
98044363Sborman * what we wanted to do in the first place...
98144363Sborman */
98244363Sborman if (his_want_state_is_will(TELOPT_ECHO) &&
98344363Sborman his_state_is_will(TELOPT_NAWS)) {
98444363Sborman while (his_will_wont_is_changing(TELOPT_ECHO))
98544363Sborman ttloop();
98644363Sborman }
98744363Sborman /*
98838995Sborman * On the off chance that the telnet client is broken and does not
98938995Sborman * respond to the DO ECHO we sent, (after all, we did send the
99038995Sborman * DO NAWS negotiation after the DO ECHO, and we won't get here
99138995Sborman * until a response to the DO NAWS comes back) simulate the
99238995Sborman * receipt of a will echo. This will also send a WONT ECHO
99338995Sborman * to the client, since we assume that the client failed to
99438995Sborman * respond because it believes that it is already in DO ECHO
99538995Sborman * mode, which we do not want.
99638995Sborman */
99744363Sborman if (his_want_state_is_will(TELOPT_ECHO)) {
99846809Sdab DIAG(TD_OPTIONS,
99946809Sdab {sprintf(nfrontp, "td: simulating recv\r\n");
100046809Sdab nfrontp += strlen(nfrontp);});
100139503Sborman willoption(TELOPT_ECHO);
100240242Sborman }
100338995Sborman
100438995Sborman /*
100538995Sborman * Finally, to clean things up, we turn on our echo. This
100638995Sborman * will break stupid 4.2 telnets out of local terminal echo.
100738995Sborman */
100838995Sborman
100944363Sborman if (my_state_is_wont(TELOPT_ECHO))
101039503Sborman send_will(TELOPT_ECHO, 1);
101138995Sborman
101257212Sdab #ifndef STREAMSPTY
101338995Sborman /*
101445234Sborman * Turn on packet mode
101538904Sborman */
101638904Sborman (void) ioctl(p, TIOCPKT, (char *)&on);
101757212Sdab #endif
101857212Sdab
101946809Sdab #if defined(LINEMODE) && defined(KLUDGELINEMODE)
102038904Sborman /*
102138904Sborman * Continuing line mode support. If client does not support
102238904Sborman * real linemode, attempt to negotiate kludge linemode by sending
102338904Sborman * the do timing mark sequence.
102438904Sborman */
102538904Sborman if (lmodetype < REAL_LINEMODE)
102639503Sborman send_do(TELOPT_TM, 1);
102746809Sdab #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */
102838904Sborman
102938904Sborman /*
103038904Sborman * Call telrcv() once to pick up anything received during
103138904Sborman * terminal type negotiation, 4.2/4.3 determination, and
103238904Sborman * linemode negotiation.
103338904Sborman */
103438904Sborman telrcv();
103538904Sborman
103638904Sborman (void) ioctl(f, FIONBIO, (char *)&on);
103738904Sborman (void) ioctl(p, FIONBIO, (char *)&on);
103840242Sborman #if defined(CRAY2) && defined(UNICOS5)
103938904Sborman init_termdriver(f, p, interrupt, sendbrk);
104038904Sborman #endif
104138904Sborman
104238904Sborman #if defined(SO_OOBINLINE)
104357212Sdab (void) setsockopt(net, SOL_SOCKET, SO_OOBINLINE,
104457212Sdab (char *)&on, sizeof on);
104538904Sborman #endif /* defined(SO_OOBINLINE) */
104638904Sborman
104738904Sborman #ifdef SIGTSTP
104838904Sborman (void) signal(SIGTSTP, SIG_IGN);
104938904Sborman #endif
105038904Sborman #ifdef SIGTTOU
105138904Sborman /*
105238904Sborman * Ignoring SIGTTOU keeps the kernel from blocking us
105338904Sborman * in ttioct() in /sys/tty.c.
105438904Sborman */
105538904Sborman (void) signal(SIGTTOU, SIG_IGN);
105638904Sborman #endif
105738904Sborman
105838904Sborman (void) signal(SIGCHLD, cleanup);
105938904Sborman
106040242Sborman #if defined(CRAY2) && defined(UNICOS5)
106138904Sborman /*
106238904Sborman * Cray-2 will send a signal when pty modes are changed by slave
106338904Sborman * side. Set up signal handler now.
106438904Sborman */
106538904Sborman if ((int)signal(SIGUSR1, termstat) < 0)
106638904Sborman perror("signal");
106738904Sborman else if (ioctl(p, TCSIGME, (char *)SIGUSR1) < 0)
106838904Sborman perror("ioctl:TCSIGME");
106938904Sborman /*
107038904Sborman * Make processing loop check terminal characteristics early on.
107138904Sborman */
107238904Sborman termstat();
107338904Sborman #endif
107438904Sborman
107545234Sborman #ifdef TIOCNOTTY
107645234Sborman {
107745234Sborman register int t;
107845234Sborman t = open(_PATH_TTY, O_RDWR);
107945234Sborman if (t >= 0) {
108045234Sborman (void) ioctl(t, TIOCNOTTY, (char *)0);
108145234Sborman (void) close(t);
108245234Sborman }
108345234Sborman }
108440242Sborman #endif
108545234Sborman
108646809Sdab #if defined(CRAY) && defined(NEWINIT) && defined(TIOCSCTTY)
108745234Sborman (void) setsid();
108844363Sborman ioctl(p, TIOCSCTTY, 0);
108944363Sborman #endif
109038904Sborman
109138904Sborman /*
109212713Ssam * Show banner that getty never gave.
109327797Sminshall *
109433271Sminshall * We put the banner in the pty input buffer. This way, it
109533271Sminshall * gets carriage return null processing, etc., just like all
109633271Sminshall * other pty --> client data.
109712713Ssam */
109827797Sminshall
109945234Sborman #if !defined(CRAY) || !defined(NEWINIT)
110045234Sborman if (getenv("USER"))
110145234Sborman hostinfo = 0;
110245234Sborman #endif
110338904Sborman
110433271Sminshall if (getent(defent, "default") == 1) {
110533271Sminshall char *getstr();
110638904Sborman char *cp=defstrs;
110727649Sminshall
110838904Sborman HE = getstr("he", &cp);
110938904Sborman HN = getstr("hn", &cp);
111038904Sborman IM = getstr("im", &cp);
111133271Sminshall if (HN && *HN)
111246809Sdab (void) strcpy(host_name, HN);
111338904Sborman if (IM == 0)
111438904Sborman IM = "";
111533271Sminshall } else {
111645234Sborman IM = DEFAULT_IM;
111738904Sborman HE = 0;
111833271Sminshall }
111946809Sdab edithost(HE, host_name);
112045234Sborman if (hostinfo && *IM)
112138904Sborman putf(IM, ptyibuf2);
112227797Sminshall
112338904Sborman if (pcc)
112438904Sborman (void) strncat(ptyibuf2, ptyip, pcc+1);
112538904Sborman ptyip = ptyibuf2;
112638904Sborman pcc = strlen(ptyip);
112740242Sborman #ifdef LINEMODE
112840242Sborman /*
112940242Sborman * Last check to make sure all our states are correct.
113040242Sborman */
113140242Sborman init_termbuf();
113240242Sborman localstat();
113340242Sborman #endif /* LINEMODE */
113433271Sminshall
113546809Sdab DIAG(TD_REPORT,
113646809Sdab {sprintf(nfrontp, "td: Entering processing loop\r\n");
113746809Sdab nfrontp += strlen(nfrontp);});
113844363Sborman
113945234Sborman #ifdef convex
114045234Sborman startslave(host);
114145234Sborman #endif
114245234Sborman
114368346Sdab nfd = ((f > p) ? f : p) + 1;
11446002Sroot for (;;) {
114527185Sminshall fd_set ibits, obits, xbits;
11466002Sroot register int c;
11476002Sroot
114827185Sminshall if (ncc < 0 && pcc < 0)
114927185Sminshall break;
115027185Sminshall
115140242Sborman #if defined(CRAY2) && defined(UNICOS5)
115238904Sborman if (needtermstat)
115338904Sborman _termstat();
115440242Sborman #endif /* defined(CRAY2) && defined(UNICOS5) */
115527185Sminshall FD_ZERO(&ibits);
115627185Sminshall FD_ZERO(&obits);
115727185Sminshall FD_ZERO(&xbits);
11586002Sroot /*
11596002Sroot * Never look for input if there's still
11606002Sroot * stuff in the corresponding output buffer
11616002Sroot */
116227185Sminshall if (nfrontp - nbackp || pcc > 0) {
116327185Sminshall FD_SET(f, &obits);
116427185Sminshall } else {
116527185Sminshall FD_SET(p, &ibits);
116627185Sminshall }
116727185Sminshall if (pfrontp - pbackp || ncc > 0) {
116827185Sminshall FD_SET(p, &obits);
116927185Sminshall } else {
117027185Sminshall FD_SET(f, &ibits);
117127185Sminshall }
117227185Sminshall if (!SYNCHing) {
117327185Sminshall FD_SET(f, &xbits);
117427185Sminshall }
117568346Sdab if ((c = select(nfd, &ibits, &obits, &xbits,
117627185Sminshall (struct timeval *)0)) < 1) {
117727185Sminshall if (c == -1) {
117827185Sminshall if (errno == EINTR) {
117927185Sminshall continue;
118027185Sminshall }
118127185Sminshall }
11826002Sroot sleep(5);
11836002Sroot continue;
11846002Sroot }
11856002Sroot
11866002Sroot /*
118727185Sminshall * Any urgent data?
118827185Sminshall */
118927185Sminshall if (FD_ISSET(net, &xbits)) {
119027185Sminshall SYNCHing = 1;
119127185Sminshall }
119227185Sminshall
119327185Sminshall /*
11946002Sroot * Something to read from the network...
11956002Sroot */
119627185Sminshall if (FD_ISSET(net, &ibits)) {
119727649Sminshall #if !defined(SO_OOBINLINE)
119827185Sminshall /*
119927898Skarels * In 4.2 (and 4.3 beta) systems, the
120027185Sminshall * OOB indication and data handling in the kernel
120127185Sminshall * is such that if two separate TCP Urgent requests
120227185Sminshall * come in, one byte of TCP data will be overlaid.
120327185Sminshall * This is fatal for Telnet, but we try to live
120427185Sminshall * with it.
120527185Sminshall *
120627185Sminshall * In addition, in 4.2 (and...), a special protocol
120727185Sminshall * is needed to pick up the TCP Urgent data in
120827185Sminshall * the correct sequence.
120927185Sminshall *
121027185Sminshall * What we do is: if we think we are in urgent
121127185Sminshall * mode, we look to see if we are "at the mark".
121227185Sminshall * If we are, we do an OOB receive. If we run
121327185Sminshall * this twice, we will do the OOB receive twice,
121427185Sminshall * but the second will fail, since the second
121527185Sminshall * time we were "at the mark", but there wasn't
121627185Sminshall * any data there (the kernel doesn't reset
121727185Sminshall * "at the mark" until we do a normal read).
121827185Sminshall * Once we've read the OOB data, we go ahead
121927185Sminshall * and do normal reads.
122027185Sminshall *
122127185Sminshall * There is also another problem, which is that
122227185Sminshall * since the OOB byte we read doesn't put us
122327185Sminshall * out of OOB state, and since that byte is most
122427185Sminshall * likely the TELNET DM (data mark), we would
122527185Sminshall * stay in the TELNET SYNCH (SYNCHing) state.
122627185Sminshall * So, clocks to the rescue. If we've "just"
122727185Sminshall * received a DM, then we test for the
122827185Sminshall * presence of OOB data when the receive OOB
122927185Sminshall * fails (and AFTER we did the normal mode read
123027185Sminshall * to clear "at the mark").
123127185Sminshall */
123227185Sminshall if (SYNCHing) {
123327185Sminshall int atmark;
123427185Sminshall
123538904Sborman (void) ioctl(net, SIOCATMARK, (char *)&atmark);
123627185Sminshall if (atmark) {
123727185Sminshall ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB);
123827185Sminshall if ((ncc == -1) && (errno == EINVAL)) {
123927185Sminshall ncc = read(net, netibuf, sizeof (netibuf));
124027983Sminshall if (sequenceIs(didnetreceive, gotDM)) {
124127185Sminshall SYNCHing = stilloob(net);
124227185Sminshall }
124327185Sminshall }
124427185Sminshall } else {
124527185Sminshall ncc = read(net, netibuf, sizeof (netibuf));
12466002Sroot }
124727185Sminshall } else {
124827185Sminshall ncc = read(net, netibuf, sizeof (netibuf));
124927185Sminshall }
125027185Sminshall settimer(didnetreceive);
125127649Sminshall #else /* !defined(SO_OOBINLINE)) */
125227185Sminshall ncc = read(net, netibuf, sizeof (netibuf));
125327649Sminshall #endif /* !defined(SO_OOBINLINE)) */
125427185Sminshall if (ncc < 0 && errno == EWOULDBLOCK)
125527185Sminshall ncc = 0;
125627185Sminshall else {
125727185Sminshall if (ncc <= 0) {
125827185Sminshall break;
125927185Sminshall }
126027185Sminshall netip = netibuf;
126127185Sminshall }
126246809Sdab DIAG((TD_REPORT | TD_NETDATA),
126346809Sdab {sprintf(nfrontp, "td: netread %d chars\r\n", ncc);
126446809Sdab nfrontp += strlen(nfrontp);});
126546809Sdab DIAG(TD_NETDATA, printdata("nd", netip, ncc));
12666002Sroot }
12676002Sroot
12686002Sroot /*
12696002Sroot * Something to read from the pty...
12706002Sroot */
127138904Sborman if (FD_ISSET(p, &ibits)) {
127257212Sdab #ifndef STREAMSPTY
12736002Sroot pcc = read(p, ptyibuf, BUFSIZ);
127457212Sdab #else
127557212Sdab pcc = readstream(p, ptyibuf, BUFSIZ);
127657212Sdab #endif
127746809Sdab /*
127846809Sdab * On some systems, if we try to read something
127946809Sdab * off the master side before the slave side is
128046809Sdab * opened, we get EIO.
128146809Sdab */
128257212Sdab if (pcc < 0 && (errno == EWOULDBLOCK ||
128357212Sdab #ifdef EAGAIN
128457212Sdab errno == EAGAIN ||
128557212Sdab #endif
128657212Sdab errno == EIO)) {
12876002Sroot pcc = 0;
128846809Sdab } else {
12896002Sroot if (pcc <= 0)
12906002Sroot break;
129140242Sborman #if !defined(CRAY2) || !defined(UNICOS5)
129238904Sborman #ifdef LINEMODE
129338904Sborman /*
129438904Sborman * If ioctl from pty, pass it through net
129538904Sborman */
129638904Sborman if (ptyibuf[0] & TIOCPKT_IOCTL) {
129738904Sborman copy_termbuf(ptyibuf+1, pcc-1);
129838904Sborman localstat();
129938904Sborman pcc = 1;
130038904Sborman }
130146809Sdab #endif /* LINEMODE */
130237210Sminshall if (ptyibuf[0] & TIOCPKT_FLUSHWRITE) {
130338904Sborman netclear(); /* clear buffer back */
130445234Sborman #ifndef NO_URGENT
130540242Sborman /*
130645234Sborman * There are client telnets on some
130740242Sborman * operating systems get screwed up
130840242Sborman * royally if we send them urgent
130945234Sborman * mode data.
131040242Sborman */
131137210Sminshall *nfrontp++ = IAC;
131237210Sminshall *nfrontp++ = DM;
131337210Sminshall neturg = nfrontp-1; /* off by one XXX */
1314*69786Sdab DIAG(TD_OPTIONS,
1315*69786Sdab printoption("td: send IAC", DM));
1316*69786Sdab
131740242Sborman #endif
131837210Sminshall }
131944363Sborman if (his_state_is_will(TELOPT_LFLOW) &&
132037210Sminshall (ptyibuf[0] &
132138904Sborman (TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))) {
132257597Sdab int newflow =
132357597Sdab ptyibuf[0] & TIOCPKT_DOSTOP ? 1 : 0;
132457597Sdab if (newflow != flowmode) {
132557597Sdab flowmode = newflow;
132657597Sdab (void) sprintf(nfrontp,
132757597Sdab "%c%c%c%c%c%c",
132857597Sdab IAC, SB, TELOPT_LFLOW,
132957597Sdab flowmode ? LFLOW_ON
133057597Sdab : LFLOW_OFF,
133157597Sdab IAC, SE);
133257597Sdab nfrontp += 6;
1333*69786Sdab DIAG(TD_OPTIONS, printsub('>',
1334*69786Sdab (unsigned char *)nfrontp-4,
1335*69786Sdab 4););
133657597Sdab }
133737210Sminshall }
133833267Sminshall pcc--;
133933267Sminshall ptyip = ptyibuf+1;
134040242Sborman #else /* defined(CRAY2) && defined(UNICOS5) */
134138904Sborman if (!uselinemode) {
134239531Sborman unpcc = pcc;
134339531Sborman unptyip = ptyibuf;
134439531Sborman pcc = term_output(&unptyip, ptyibuf2,
134539531Sborman &unpcc, BUFSIZ);
134638904Sborman ptyip = ptyibuf2;
134738904Sborman } else
134838904Sborman ptyip = ptyibuf;
134940242Sborman #endif /* defined(CRAY2) && defined(UNICOS5) */
135038904Sborman }
13516002Sroot }
13526002Sroot
13536002Sroot while (pcc > 0) {
13546002Sroot if ((&netobuf[BUFSIZ] - nfrontp) < 2)
13556002Sroot break;
13566002Sroot c = *ptyip++ & 0377, pcc--;
13576002Sroot if (c == IAC)
13586002Sroot *nfrontp++ = c;
135940242Sborman #if defined(CRAY2) && defined(UNICOS5)
136038904Sborman else if (c == '\n' &&
136144363Sborman my_state_is_wont(TELOPT_BINARY) && newmap)
136238904Sborman *nfrontp++ = '\r';
136340242Sborman #endif /* defined(CRAY2) && defined(UNICOS5) */
13646002Sroot *nfrontp++ = c;
136544363Sborman if ((c == '\r') && (my_state_is_wont(TELOPT_BINARY))) {
136627020Sminshall if (pcc > 0 && ((*ptyip & 0377) == '\n')) {
136727020Sminshall *nfrontp++ = *ptyip++ & 0377;
136827020Sminshall pcc--;
136927020Sminshall } else
137027020Sminshall *nfrontp++ = '\0';
137127020Sminshall }
13726002Sroot }
137340242Sborman #if defined(CRAY2) && defined(UNICOS5)
137439531Sborman /*
137539531Sborman * If chars were left over from the terminal driver,
137639531Sborman * note their existence.
137739531Sborman */
137846809Sdab if (!uselinemode && unpcc) {
137939531Sborman pcc = unpcc;
138039531Sborman unpcc = 0;
138139531Sborman ptyip = unptyip;
138239531Sborman }
138340242Sborman #endif /* defined(CRAY2) && defined(UNICOS5) */
138439531Sborman
138527185Sminshall if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0)
13866002Sroot netflush();
13876002Sroot if (ncc > 0)
13886002Sroot telrcv();
138927185Sminshall if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0)
13906002Sroot ptyflush();
13916002Sroot }
139246809Sdab cleanup(0);
139338904Sborman } /* end of telnet */
1394*69786Sdab
139538904Sborman #ifndef TCSIG
139638904Sborman # ifdef TIOCSIG
139738904Sborman # define TCSIG TIOCSIG
139838904Sborman # endif
139938904Sborman #endif
14006002Sroot
140157212Sdab #ifdef STREAMSPTY
140257212Sdab
140357212Sdab int flowison = -1; /* current state of flow: -1 is unknown */
140457212Sdab
readstream(p,ibuf,bufsize)140557212Sdab int readstream(p, ibuf, bufsize)
140657212Sdab int p;
140757212Sdab char *ibuf;
140857212Sdab int bufsize;
140957212Sdab {
141057212Sdab int flags = 0;
141157212Sdab int ret = 0;
141257212Sdab struct termios *tsp;
141357212Sdab struct termio *tp;
141457212Sdab struct iocblk *ip;
141557212Sdab char vstop, vstart;
141657212Sdab int ixon;
141757212Sdab int newflow;
141857212Sdab
141957212Sdab strbufc.maxlen = BUFSIZ;
142065158Sdab strbufc.buf = (char *)ctlbuf;
142157212Sdab strbufd.maxlen = bufsize-1;
142257212Sdab strbufd.len = 0;
142357212Sdab strbufd.buf = ibuf+1;
142457212Sdab ibuf[0] = 0;
142557212Sdab
142657212Sdab ret = getmsg(p, &strbufc, &strbufd, &flags);
142757212Sdab if (ret < 0) /* error of some sort -- probably EAGAIN */
142857212Sdab return(-1);
142957212Sdab
143057212Sdab if (strbufc.len <= 0 || ctlbuf[0] == M_DATA) {
143157212Sdab /* data message */
143257212Sdab if (strbufd.len > 0) { /* real data */
143357212Sdab return(strbufd.len + 1); /* count header char */
143457212Sdab } else {
143557212Sdab /* nothing there */
143657212Sdab errno = EAGAIN;
143757212Sdab return(-1);
143857212Sdab }
143957212Sdab }
144057212Sdab
144157212Sdab /*
144257212Sdab * It's a control message. Return 1, to look at the flag we set
144357212Sdab */
144457212Sdab
144557212Sdab switch (ctlbuf[0]) {
144657212Sdab case M_FLUSH:
144757212Sdab if (ibuf[1] & FLUSHW)
144857212Sdab ibuf[0] = TIOCPKT_FLUSHWRITE;
144957212Sdab return(1);
145057212Sdab
145157212Sdab case M_IOCTL:
145257212Sdab ip = (struct iocblk *) (ibuf+1);
145357212Sdab
145457212Sdab switch (ip->ioc_cmd) {
145557212Sdab case TCSETS:
145657212Sdab case TCSETSW:
145757212Sdab case TCSETSF:
145857212Sdab tsp = (struct termios *)
145957212Sdab (ibuf+1 + sizeof(struct iocblk));
146057212Sdab vstop = tsp->c_cc[VSTOP];
146157212Sdab vstart = tsp->c_cc[VSTART];
146257212Sdab ixon = tsp->c_iflag & IXON;
146357212Sdab break;
146457212Sdab case TCSETA:
146557212Sdab case TCSETAW:
146657212Sdab case TCSETAF:
146757212Sdab tp = (struct termio *) (ibuf+1 + sizeof(struct iocblk));
146857212Sdab vstop = tp->c_cc[VSTOP];
146957212Sdab vstart = tp->c_cc[VSTART];
1470*69786Sdab ixon = tp->c_iflag & IXON;
147157212Sdab break;
147257212Sdab default:
147357212Sdab errno = EAGAIN;
147457212Sdab return(-1);
147557212Sdab }
147657212Sdab
147757212Sdab newflow = (ixon && (vstart == 021) && (vstop == 023)) ? 1 : 0;
147857212Sdab if (newflow != flowison) { /* it's a change */
147957212Sdab flowison = newflow;
148057212Sdab ibuf[0] = newflow ? TIOCPKT_DOSTOP : TIOCPKT_NOSTOP;
148157212Sdab return(1);
148257212Sdab }
148357212Sdab }
148457212Sdab
148557212Sdab /* nothing worth doing anything about */
148657212Sdab errno = EAGAIN;
148757212Sdab return(-1);
148857212Sdab }
148957212Sdab #endif /* STREAMSPTY */
149057212Sdab
149137212Sminshall /*
14926002Sroot * Send interrupt to process on other side of pty.
14936002Sroot * If it is in raw mode, just write NULL;
14946002Sroot * otherwise, write intr char.
14956002Sroot */
149646809Sdab void
interrupt()14976002Sroot interrupt()
14986002Sroot {
149938904Sborman ptyflush(); /* half-hearted */
15006002Sroot
150168346Sdab #if defined(STREAMSPTY) && defined(TIOCSIGNAL)
150268346Sdab /* Streams PTY style ioctl to post a signal */
150368346Sdab {
150468346Sdab int sig = SIGINT;
150568346Sdab (void) ioctl(pty, TIOCSIGNAL, &sig);
150668346Sdab (void) ioctl(pty, I_FLUSH, FLUSHR);
150768346Sdab }
150868346Sdab #else
150938904Sborman #ifdef TCSIG
151038904Sborman (void) ioctl(pty, TCSIG, (char *)SIGINT);
151138904Sborman #else /* TCSIG */
151238904Sborman init_termbuf();
151340242Sborman *pfrontp++ = slctab[SLC_IP].sptr ?
151440242Sborman (unsigned char)*slctab[SLC_IP].sptr : '\177';
151538904Sborman #endif /* TCSIG */
151668346Sdab #endif
15176002Sroot }
15186002Sroot
151927229Sminshall /*
152027229Sminshall * Send quit to process on other side of pty.
152127229Sminshall * If it is in raw mode, just write NULL;
152227229Sminshall * otherwise, write quit char.
152327229Sminshall */
152446809Sdab void
sendbrk()152527229Sminshall sendbrk()
152627229Sminshall {
152727229Sminshall ptyflush(); /* half-hearted */
152838904Sborman #ifdef TCSIG
152938904Sborman (void) ioctl(pty, TCSIG, (char *)SIGQUIT);
153038904Sborman #else /* TCSIG */
153138904Sborman init_termbuf();
153240242Sborman *pfrontp++ = slctab[SLC_ABORT].sptr ?
153340242Sborman (unsigned char)*slctab[SLC_ABORT].sptr : '\034';
153438904Sborman #endif /* TCSIG */
153527229Sminshall }
153627229Sminshall
153746809Sdab void
sendsusp()153838904Sborman sendsusp()
15396002Sroot {
154038904Sborman #ifdef SIGTSTP
154138904Sborman ptyflush(); /* half-hearted */
154238904Sborman # ifdef TCSIG
154338904Sborman (void) ioctl(pty, TCSIG, (char *)SIGTSTP);
154438904Sborman # else /* TCSIG */
154540242Sborman *pfrontp++ = slctab[SLC_SUSP].sptr ?
154640242Sborman (unsigned char)*slctab[SLC_SUSP].sptr : '\032';
154738904Sborman # endif /* TCSIG */
154838904Sborman #endif /* SIGTSTP */
15496002Sroot }
15506002Sroot
155145234Sborman /*
155245234Sborman * When we get an AYT, if ^T is enabled, use that. Otherwise,
155345234Sborman * just send back "[Yes]".
155445234Sborman */
155546809Sdab void
recv_ayt()155645234Sborman recv_ayt()
155745234Sborman {
155845234Sborman #if defined(SIGINFO) && defined(TCSIG)
155945234Sborman if (slctab[SLC_AYT].sptr && *slctab[SLC_AYT].sptr != _POSIX_VDISABLE) {
156045234Sborman (void) ioctl(pty, TCSIG, (char *)SIGINFO);
156145234Sborman return;
156245234Sborman }
156345234Sborman #endif
156445234Sborman (void) strcpy(nfrontp, "\r\n[Yes]\r\n");
156545234Sborman nfrontp += 9;
156645234Sborman }
156745234Sborman
156846809Sdab void
doeof()156938904Sborman doeof()
15706002Sroot {
157138904Sborman init_termbuf();
15726002Sroot
157345234Sborman #if defined(LINEMODE) && defined(USE_TERMIO) && (VEOF == VMIN)
157440242Sborman if (!tty_isediting()) {
157546809Sdab extern char oldeofc;
157640242Sborman *pfrontp++ = oldeofc;
157740242Sborman return;
157840242Sborman }
157940242Sborman #endif
158040242Sborman *pfrontp++ = slctab[SLC_EOF].sptr ?
158140242Sborman (unsigned char)*slctab[SLC_EOF].sptr : '\004';
15826002Sroot }
1583