121182Sdist /* 238904Sborman * Copyright (c) 1989 Regents of the University of California. 333687Sbostic * All rights reserved. 433687Sbostic * 542673Sbostic * %sccs.include.redist.c% 621182Sdist */ 721182Sdist 86295Sroot #ifndef lint 921182Sdist char copyright[] = 1038904Sborman "@(#) Copyright (c) 1989 Regents of the University of California.\n\ 1121182Sdist All rights reserved.\n"; 1233687Sbostic #endif /* not lint */ 136295Sroot 1421182Sdist #ifndef lint 15*44363Sborman static char sccsid[] = "@(#)telnetd.c 5.45 (Berkeley) 06/28/90"; 1633687Sbostic #endif /* not lint */ 1721182Sdist 1838904Sborman #include "telnetd.h" 1938904Sborman 206002Sroot /* 2138904Sborman * I/O data buffers, 2238904Sborman * pointers, and counters. 236002Sroot */ 2438904Sborman char ptyibuf[BUFSIZ], *ptyip = ptyibuf; 2538904Sborman char ptyibuf2[BUFSIZ]; 269218Ssam 2738904Sborman #ifdef CRAY 2838904Sborman int hostinfo = 1; /* do we print login banner? */ 2938904Sborman #endif 309218Ssam 3138904Sborman #ifdef CRAY 3238904Sborman extern int newmap; /* nonzero if \n maps to ^M^J */ 3340242Sborman int lowpty = 0, highpty; /* low, high pty numbers */ 3438904Sborman #endif /* CRAY */ 3512216Ssam 3638904Sborman int debug = 0; 3738904Sborman char *progname; 389218Ssam 39*44363Sborman #if defined(NEED_GETTOS) 40*44363Sborman struct tosent { 41*44363Sborman char *t_name; /* name */ 42*44363Sborman char **t_aliases; /* alias list */ 43*44363Sborman char *t_proto; /* protocol */ 44*44363Sborman int t_tos; /* Type Of Service bits */ 45*44363Sborman }; 46*44363Sborman 47*44363Sborman struct tosent * 48*44363Sborman gettosbyname(name, proto) 49*44363Sborman char *name, *proto; 50*44363Sborman { 51*44363Sborman static struct tosent te; 52*44363Sborman static char *aliasp = 0; 53*44363Sborman 54*44363Sborman te.t_name = name; 55*44363Sborman te.t_aliases = &aliasp; 56*44363Sborman te.t_proto = proto; 57*44363Sborman te.t_tos = 020; /* Low Delay bit */ 58*44363Sborman return(&te); 59*44363Sborman } 60*44363Sborman #endif 61*44363Sborman 6238904Sborman main(argc, argv) 6338904Sborman char *argv[]; 6438904Sborman { 6538904Sborman struct sockaddr_in from; 6638904Sborman int on = 1, fromlen; 67*44363Sborman #if defined(HAS_IP_TOS) || defined(NEED_GETTOS) 6840242Sborman struct tosent *tp; 69*44363Sborman #endif /* defined(HAS_IP_TOS) || defined(NEED_GETTOS) */ 706002Sroot 7138904Sborman pfrontp = pbackp = ptyobuf; 7238904Sborman netip = netibuf; 7338904Sborman nfrontp = nbackp = netobuf; 746002Sroot 7538904Sborman progname = *argv; 7640242Sborman 7740242Sborman #ifdef CRAY 7840242Sborman /* 7940242Sborman * Get number of pty's before trying to process options, 8040242Sborman * which may include changing pty range. 8140242Sborman */ 8240242Sborman highpty = getnpty(); 8340242Sborman #endif /* CRAY */ 8440242Sborman 8538904Sborman top: 8638904Sborman argc--, argv++; 8727649Sminshall 8838904Sborman if (argc > 0 && strcmp(*argv, "-debug") == 0) { 8938904Sborman debug++; 9038904Sborman goto top; 9138904Sborman } 9227649Sminshall 9338904Sborman #ifdef LINEMODE 9438904Sborman if (argc > 0 && !strcmp(*argv, "-l")) { 9538904Sborman alwayslinemode = 1; 9638904Sborman goto top; 9738904Sborman } 9838904Sborman #endif /* LINEMODE */ 9927649Sminshall 10038904Sborman #ifdef CRAY 10138904Sborman if (argc > 0 && !strcmp(*argv, "-h")) { 10238904Sborman hostinfo = 0; 10338904Sborman goto top; 10438904Sborman } 10527649Sminshall 10638904Sborman if (argc > 0 && !strncmp(*argv, "-r", 2)) { 10738904Sborman char *strchr(); 10838904Sborman char *c; 10927649Sminshall 11040242Sborman /* 11140242Sborman * Allow the specification of alterations to the pty search 11240242Sborman * range. It is legal to specify only one, and not change the 11340242Sborman * other from its default. 11440242Sborman */ 11538904Sborman *argv += 2; 11638904Sborman if (**argv == '\0' && argc) 11738904Sborman argv++, argc--; 11838904Sborman c = strchr(*argv, '-'); 11938904Sborman if (c) { 12038904Sborman *c++ = '\0'; 12138904Sborman highpty = atoi(c); 12240242Sborman } 12340242Sborman if (**argv != '\0') 12440242Sborman lowpty = atoi(*argv); 12540242Sborman if ((lowpty > highpty) || (lowpty < 0) || (highpty > 32767)) { 126*44363Sborman usage(); 127*44363Sborman /* NOT REACHED */ 12838904Sborman } 12938904Sborman goto top; 13038904Sborman } 13138904Sborman # ifdef NEWINIT 13238904Sborman if (argc > 0 && !strncmp(*argv, "-I", 2)) { 13338904Sborman extern char *gen_id; 13427649Sminshall 13538904Sborman *argv += 2; 13638904Sborman if (**argv == '\0') { 137*44363Sborman if (argc < 2) { 138*44363Sborman usage(); 139*44363Sborman /* NOT REACHED */ 140*44363Sborman } 14138904Sborman argv++, argc--; 142*44363Sborman if (**argv == '\0') { 143*44363Sborman usage(); 144*44363Sborman /* NOT REACHED */ 145*44363Sborman } 14638904Sborman } 14738904Sborman gen_id = *argv; 14838904Sborman goto top; 14938904Sborman } 15038904Sborman # endif /* NEWINIT */ 15138904Sborman #endif /* CRAY */ 1526002Sroot 153*44363Sborman #ifdef DIAGNOSTICS 154*44363Sborman /* 155*44363Sborman * Check for desired diagnostics capabilities. 156*44363Sborman */ 157*44363Sborman if (argc > 0 && !strncmp(*argv, "-D", 2)) { 158*44363Sborman *argv += 2; 159*44363Sborman if (**argv == '\0') { 160*44363Sborman if (argc < 2) { 161*44363Sborman usage(); 162*44363Sborman /* NOT REACHED */ 163*44363Sborman } 164*44363Sborman argv++, argc--; 165*44363Sborman if (**argv == '\0') { 166*44363Sborman usage(); 167*44363Sborman /* NOT REACHED */ 168*44363Sborman } 169*44363Sborman } 170*44363Sborman if (!strcmp(*argv, "report")) { 171*44363Sborman diagnostic |= TD_REPORT|TD_OPTIONS; 172*44363Sborman } else if (!strcmp(*argv, "exercise")) { 173*44363Sborman diagnostic |= TD_EXERCISE; 174*44363Sborman } else if (!strcmp(*argv, "netdata")) { 175*44363Sborman diagnostic |= TD_NETDATA; 176*44363Sborman } else if (!strcmp(*argv, "ptydata")) { 177*44363Sborman diagnostic |= TD_PTYDATA; 178*44363Sborman } else if (!strcmp(*argv, "options")) { 179*44363Sborman diagnostic |= TD_OPTIONS; 180*44363Sborman } else { 181*44363Sborman usage(); 182*44363Sborman /* NOT REACHED */ 183*44363Sborman } 184*44363Sborman goto top; 185*44363Sborman } 186*44363Sborman #endif /* DIAGNOSTICS */ 187*44363Sborman 188*44363Sborman #ifdef BFTPDAEMON 189*44363Sborman /* 190*44363Sborman * Check for bftp daemon 191*44363Sborman */ 192*44363Sborman if (argc > 0 && !strncmp(*argv, "-B", 2)) { 193*44363Sborman bftpd++; 194*44363Sborman goto top; 195*44363Sborman } 196*44363Sborman #endif /* BFTPDAEMON */ 197*44363Sborman 198*44363Sborman if (argc > 0 && **argv == '-') { 199*44363Sborman fprintf(stderr, "telnetd: %s: unknown option\n", *argv+1); 200*44363Sborman usage(); 201*44363Sborman /* NOT REACHED */ 202*44363Sborman } 203*44363Sborman 20438904Sborman if (debug) { 20527185Sminshall int s, ns, foo; 20627185Sminshall struct servent *sp; 20727185Sminshall static struct sockaddr_in sin = { AF_INET }; 20827185Sminshall 209*44363Sborman if (argc > 1) { 210*44363Sborman usage(); 211*44363Sborman /* NOT REACHED */ 212*44363Sborman } else if (argc == 1) { 21338904Sborman if (sp = getservbyname(*argv, "tcp")) { 21438904Sborman sin.sin_port = sp->s_port; 21538904Sborman } else { 21638904Sborman sin.sin_port = atoi(*argv); 21738904Sborman if ((int)sin.sin_port <= 0) { 21838904Sborman fprintf(stderr, "telnetd: %s: bad port #\n", *argv); 219*44363Sborman usage(); 220*44363Sborman /* NOT REACHED */ 22138904Sborman } 22238904Sborman sin.sin_port = htons((u_short)sin.sin_port); 22338904Sborman } 22437210Sminshall } else { 22537210Sminshall sp = getservbyname("telnet", "tcp"); 22637210Sminshall if (sp == 0) { 227*44363Sborman fprintf(stderr, "telnetd: tcp/telnet: unknown service\n"); 22838904Sborman exit(1); 22937210Sminshall } 23037210Sminshall sin.sin_port = sp->s_port; 23127185Sminshall } 23227185Sminshall 23327185Sminshall s = socket(AF_INET, SOCK_STREAM, 0); 23427185Sminshall if (s < 0) { 23527185Sminshall perror("telnetd: socket");; 23627185Sminshall exit(1); 23727185Sminshall } 23838904Sborman (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); 23938904Sborman if (bind(s, (struct sockaddr *)&sin, sizeof sin) < 0) { 24027185Sminshall perror("bind"); 24127185Sminshall exit(1); 24227185Sminshall } 24327185Sminshall if (listen(s, 1) < 0) { 24427185Sminshall perror("listen"); 24527185Sminshall exit(1); 24627185Sminshall } 24727185Sminshall foo = sizeof sin; 24838904Sborman ns = accept(s, (struct sockaddr *)&sin, &foo); 24927185Sminshall if (ns < 0) { 25027185Sminshall perror("accept"); 25127185Sminshall exit(1); 25227185Sminshall } 25338904Sborman (void) dup2(ns, 0); 25438904Sborman (void) close(ns); 25538904Sborman (void) close(s); 256*44363Sborman } else if (argc > 0) { 257*44363Sborman usage(); 258*44363Sborman /* NOT REACHED */ 25927185Sminshall } 26038904Sborman 26124855Seric openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON); 26216371Skarels fromlen = sizeof (from); 26338904Sborman if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) { 26438904Sborman fprintf(stderr, "%s: ", progname); 26516371Skarels perror("getpeername"); 26616371Skarels _exit(1); 2678346Ssam } 26817156Ssam if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) { 26917187Sralph syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); 27010418Ssam } 27140242Sborman 272*44363Sborman #if defined(HAS_IP_TOS) || defined(NEED_GETTOS) 273*44363Sborman if ((tp = gettosbyname("telnet", "tcp")) && 274*44363Sborman (setsockopt(0, IPPROTO_IP, IP_TOS, &tp->t_tos, sizeof(int)) < 0)) 27540242Sborman syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); 276*44363Sborman #endif /* defined(HAS_IP_TOS) || defined(NEED_GETTOS) */ 27738904Sborman net = 0; 27838904Sborman doit(&from); 27938904Sborman /* NOTREACHED */ 28038904Sborman } /* end of main */ 2816002Sroot 282*44363Sborman usage() 283*44363Sborman { 284*44363Sborman fprintf(stderr, "Usage: telnetd [-debug] [-h]"); 285*44363Sborman #ifdef NEWINIT 286*44363Sborman fprintf(stderr, " [-Iinitid]"); 287*44363Sborman #endif /* NEWINIT */ 288*44363Sborman #ifdef DIAGNOSTICS 289*44363Sborman fprintf(stderr, " [-D (options|report|exercise|netdata|ptydata)]"); 290*44363Sborman #endif /* DIAGNOSTICS */ 291*44363Sborman #ifdef LINEMODE 292*44363Sborman fprintf(stderr, " [-l]"); 293*44363Sborman #endif 294*44363Sborman #ifdef CRAY 295*44363Sborman fprintf(stderr, " [-r[lowpty]-[highpty]]"); 296*44363Sborman #endif 297*44363Sborman #ifdef BFTPDAEMON 298*44363Sborman fprintf(stderr, " [-B]"); 299*44363Sborman #endif /* BFTPDAEMON */ 300*44363Sborman fprintf(stderr, " [port]\n"); 301*44363Sborman exit(1); 302*44363Sborman } 303*44363Sborman 30440242Sborman void cleanup(); 30527649Sminshall 30627649Sminshall /* 30727983Sminshall * getterminaltype 30827649Sminshall * 30938904Sborman * Ask the other end to send along its terminal type and speed. 31027983Sminshall * Output is the variable terminaltype filled in. 31127649Sminshall */ 31238904Sborman static char ttytype_sbbuf[] = { IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE }; 31327983Sminshall void 31427983Sminshall getterminaltype() 31527649Sminshall { 31638904Sborman void ttloop(); 31727649Sminshall 31838904Sborman settimer(baseline); 31939503Sborman send_do(TELOPT_TTYPE, 1); 32039503Sborman send_do(TELOPT_TSPEED, 1); 321*44363Sborman send_do(TELOPT_XDISPLOC, 1); 322*44363Sborman send_do(TELOPT_ENVIRON, 1); 323*44363Sborman while (his_will_wont_is_changing(TELOPT_TTYPE) || 324*44363Sborman his_will_wont_is_changing(TELOPT_TSPEED) || 325*44363Sborman his_will_wont_is_changing(TELOPT_XDISPLOC) || 326*44363Sborman his_will_wont_is_changing(TELOPT_ENVIRON)) { 32727983Sminshall ttloop(); 32827649Sminshall } 329*44363Sborman if (his_state_is_will(TELOPT_TSPEED)) { 33038904Sborman static char sbbuf[] = { IAC, SB, TELOPT_TSPEED, TELQUAL_SEND, IAC, SE }; 33127983Sminshall 33227983Sminshall bcopy(sbbuf, nfrontp, sizeof sbbuf); 33327983Sminshall nfrontp += sizeof sbbuf; 33438904Sborman } 335*44363Sborman if (his_state_is_will(TELOPT_XDISPLOC)) { 336*44363Sborman static char sbbuf[] = { IAC, SB, TELOPT_XDISPLOC, TELQUAL_SEND, IAC, SE }; 33738904Sborman 338*44363Sborman bcopy(sbbuf, nfrontp, sizeof sbbuf); 339*44363Sborman nfrontp += sizeof sbbuf; 340*44363Sborman } 341*44363Sborman if (his_state_is_will(TELOPT_ENVIRON)) { 342*44363Sborman static char sbbuf[] = { IAC, SB, TELOPT_ENVIRON, TELQUAL_SEND, IAC, SE }; 343*44363Sborman 344*44363Sborman bcopy(sbbuf, nfrontp, sizeof sbbuf); 345*44363Sborman nfrontp += sizeof sbbuf; 346*44363Sborman } 347*44363Sborman if (his_state_is_will(TELOPT_TTYPE)) { 348*44363Sborman 34938904Sborman bcopy(ttytype_sbbuf, nfrontp, sizeof ttytype_sbbuf); 35038904Sborman nfrontp += sizeof ttytype_sbbuf; 35138904Sborman } 352*44363Sborman if (his_state_is_will(TELOPT_TSPEED)) { 35338904Sborman while (sequenceIs(tspeedsubopt, baseline)) 35427983Sminshall ttloop(); 35538904Sborman } 356*44363Sborman if (his_state_is_will(TELOPT_XDISPLOC)) { 357*44363Sborman while (sequenceIs(xdisplocsubopt, baseline)) 358*44363Sborman ttloop(); 359*44363Sborman } 360*44363Sborman if (his_state_is_will(TELOPT_ENVIRON)) { 361*44363Sborman while (sequenceIs(environsubopt, baseline)) 362*44363Sborman ttloop(); 363*44363Sborman } 364*44363Sborman if (his_state_is_will(TELOPT_TTYPE)) { 36538904Sborman char first[256], last[256]; 36638904Sborman 36738904Sborman while (sequenceIs(ttypesubopt, baseline)) 36838904Sborman ttloop(); 36938904Sborman 370*44363Sborman /* 371*44363Sborman * If the other side has already disabled the option, then 372*44363Sborman * we have to just go with what we (might) have already gotten. 373*44363Sborman */ 374*44363Sborman if (his_state_is_will(TELOPT_TTYPE) && !terminaltypeok(terminaltype)) { 37538904Sborman (void) strncpy(first, terminaltype, sizeof(first)); 37638904Sborman for(;;) { 37738904Sborman /* 37838904Sborman * Save the unknown name, and request the next name. 37938904Sborman */ 38038904Sborman (void) strncpy(last, terminaltype, sizeof(last)); 38138904Sborman _gettermname(); 382*44363Sborman if (terminaltypeok(terminaltype)) 38338904Sborman break; 384*44363Sborman if ((strncmp(last, terminaltype, sizeof(last)) == 0) || 385*44363Sborman his_state_is_wont(TELOPT_TTYPE)) { 38638904Sborman /* 38738904Sborman * We've hit the end. If this is the same as 38838904Sborman * the first name, just go with it. 38938904Sborman */ 39038904Sborman if (strncmp(first, terminaltype, sizeof(first) == 0)) 39138904Sborman break; 39238904Sborman /* 393*44363Sborman * Get the terminal name one more time, so that 39438904Sborman * RFC1091 compliant telnets will cycle back to 39538904Sborman * the start of the list. 39638904Sborman */ 397*44363Sborman _gettermname(); 39838904Sborman if (strncmp(first, terminaltype, sizeof(first) != 0)) 39938904Sborman (void) strncpy(terminaltype, first, sizeof(first)); 40038904Sborman break; 40138904Sborman } 40238904Sborman } 40327983Sminshall } 40427983Sminshall } 40538904Sborman } /* end of getterminaltype */ 40638904Sborman 40738904Sborman _gettermname() 40838904Sborman { 409*44363Sborman /* 410*44363Sborman * If the client turned off the option, 411*44363Sborman * we can't send another request, so we 412*44363Sborman * just return. 413*44363Sborman */ 414*44363Sborman if (his_state_is_wont(TELOPT_TTYPE)) 415*44363Sborman return; 41638904Sborman settimer(baseline); 41738904Sborman bcopy(ttytype_sbbuf, nfrontp, sizeof ttytype_sbbuf); 41838904Sborman nfrontp += sizeof ttytype_sbbuf; 41938904Sborman while (sequenceIs(ttypesubopt, baseline)) 42038904Sborman ttloop(); 42127649Sminshall } 42227649Sminshall 42338904Sborman terminaltypeok(s) 42438904Sborman char *s; 42538904Sborman { 42638904Sborman char buf[1024]; 42738904Sborman 42838904Sborman if (terminaltype == NULL) 42938904Sborman return(1); 43038904Sborman 43138904Sborman /* 43238904Sborman * tgetent() will return 1 if the type is known, and 43338904Sborman * 0 if it is not known. If it returns -1, it couldn't 43438904Sborman * open the database. But if we can't open the database, 43538904Sborman * it won't help to say we failed, because we won't be 43638904Sborman * able to verify anything else. So, we treat -1 like 1. 43738904Sborman */ 43838904Sborman if (tgetent(buf, s) == 0) 43938904Sborman return(0); 44038904Sborman return(1); 44138904Sborman } 44238904Sborman 4436002Sroot /* 4446002Sroot * Get a pty, scan input lines. 4456002Sroot */ 44638904Sborman doit(who) 44712683Ssam struct sockaddr_in *who; 4486002Sroot { 44920188Skarels char *host, *inet_ntoa(); 45038904Sborman int t; 45112683Ssam struct hostent *hp; 4526002Sroot 45338904Sborman /* 45438904Sborman * Find an available pty to use. 45538904Sborman */ 45638904Sborman pty = getpty(); 45738904Sborman if (pty < 0) 45838904Sborman fatal(net, "All network ports in use"); 45920188Skarels 46038904Sborman t = getptyslave(); 46138904Sborman 46238904Sborman /* get name of connected client */ 46338904Sborman hp = gethostbyaddr((char *)&who->sin_addr, sizeof (struct in_addr), 46412683Ssam who->sin_family); 46512683Ssam if (hp) 46612683Ssam host = hp->h_name; 46712683Ssam else 46817444Sralph host = inet_ntoa(who->sin_addr); 46927983Sminshall 470*44363Sborman init_env(); 47127983Sminshall /* 47238904Sborman * get terminal type. 47327983Sminshall */ 47427983Sminshall getterminaltype(); 475*44363Sborman setenv("TERM", terminaltype ? terminaltype : "network", 1); 47627983Sminshall 47727649Sminshall /* 47838904Sborman * Start up the login process on the slave side of the terminal 47927649Sminshall */ 48038904Sborman startslave(t, host); 48138904Sborman 48238904Sborman telnet(net, pty); /* begin server processing */ 4839244Ssam /*NOTREACHED*/ 48438904Sborman } /* end of doit */ 4859244Ssam 48638904Sborman #ifndef MAXHOSTNAMELEN 48738904Sborman #define MAXHOSTNAMELEN 64 48838904Sborman #endif MAXHOSTNAMELEN 4896002Sroot /* 4906002Sroot * Main loop. Select from pty and network, and 4916002Sroot * hand data to telnet receiver finite state machine. 4926002Sroot */ 4936002Sroot telnet(f, p) 49438904Sborman int f, p; 4956002Sroot { 4966002Sroot int on = 1; 49727898Skarels char hostname[MAXHOSTNAMELEN]; 49840242Sborman #if defined(CRAY2) && defined(UNICOS5) 49938904Sborman int termstat(); 50038904Sborman int interrupt(), sendbrk(); 50138904Sborman #endif 50233271Sminshall #define TABBUFSIZ 512 50333271Sminshall char defent[TABBUFSIZ]; 50433271Sminshall char defstrs[TABBUFSIZ]; 50533271Sminshall #undef TABBUFSIZ 50633271Sminshall char *HE; 50733271Sminshall char *HN; 50833271Sminshall char *IM; 50938904Sborman void netflush(); 51038904Sborman 51132400Sminshall /* 51238904Sborman * Initialize the slc mapping table. 51332400Sminshall */ 51438904Sborman get_slc_defaults(); 5156002Sroot 5168379Ssam /* 51738904Sborman * Do some tests where it is desireable to wait for a response. 51838904Sborman * Rather than doing them slowly, one at a time, do them all 51938904Sborman * at once. 5208379Ssam */ 521*44363Sborman if (my_state_is_wont(TELOPT_SGA)) 52239503Sborman send_will(TELOPT_SGA, 1); 52312713Ssam /* 52427649Sminshall * Is the client side a 4.2 (NOT 4.3) system? We need to know this 52527649Sminshall * because 4.2 clients are unable to deal with TCP urgent data. 52627649Sminshall * 52727649Sminshall * To find out, we send out a "DO ECHO". If the remote system 52827649Sminshall * answers "WILL ECHO" it is probably a 4.2 client, and we note 52927649Sminshall * that fact ("WILL ECHO" ==> that the client will echo what 53027649Sminshall * WE, the server, sends it; it does NOT mean that the client will 53127649Sminshall * echo the terminal input). 53227649Sminshall */ 53339503Sborman send_do(TELOPT_ECHO, 1); 53427649Sminshall 53538904Sborman #ifdef LINEMODE 536*44363Sborman if (his_state_is_wont(TELOPT_LINEMODE)) { 53738904Sborman /* Query the peer for linemode support by trying to negotiate 53838904Sborman * the linemode option. 53938904Sborman */ 540*44363Sborman linemode = 0; 54138904Sborman editmode = 0; 54239503Sborman send_do(TELOPT_LINEMODE, 1); /* send do linemode */ 54338904Sborman } 54438904Sborman #endif /* LINEMODE */ 54538904Sborman 54627649Sminshall /* 54738904Sborman * Send along a couple of other options that we wish to negotiate. 54838904Sborman */ 54939503Sborman send_do(TELOPT_NAWS, 1); 55039503Sborman send_will(TELOPT_STATUS, 1); 55138904Sborman flowmode = 1; /* default flow control state */ 55239503Sborman send_do(TELOPT_LFLOW, 1); 55338904Sborman 55438904Sborman /* 55538904Sborman * Spin, waiting for a response from the DO ECHO. However, 55638904Sborman * some REALLY DUMB telnets out there might not respond 55738904Sborman * to the DO ECHO. So, we spin looking for NAWS, (most dumb 55838904Sborman * telnets so far seem to respond with WONT for a DO that 55938904Sborman * they don't understand...) because by the time we get the 56038904Sborman * response, it will already have processed the DO ECHO. 56138904Sborman * Kludge upon kludge. 56238904Sborman */ 563*44363Sborman while (his_will_wont_is_changing(TELOPT_NAWS)) 56438904Sborman ttloop(); 56538904Sborman 56638904Sborman /* 567*44363Sborman * But... 568*44363Sborman * The client might have sent a WILL NAWS as part of its 569*44363Sborman * startup code; if so, we'll be here before we get the 570*44363Sborman * response to the DO ECHO. We'll make the assumption 571*44363Sborman * that any implementation that understands about NAWS 572*44363Sborman * is a modern enough implementation that it will respond 573*44363Sborman * to our DO ECHO request; hence we'll do another spin 574*44363Sborman * waiting for the ECHO option to settle down, which is 575*44363Sborman * what we wanted to do in the first place... 576*44363Sborman */ 577*44363Sborman if (his_want_state_is_will(TELOPT_ECHO) && 578*44363Sborman his_state_is_will(TELOPT_NAWS)) { 579*44363Sborman while (his_will_wont_is_changing(TELOPT_ECHO)) 580*44363Sborman ttloop(); 581*44363Sborman } 582*44363Sborman /* 58338995Sborman * On the off chance that the telnet client is broken and does not 58438995Sborman * respond to the DO ECHO we sent, (after all, we did send the 58538995Sborman * DO NAWS negotiation after the DO ECHO, and we won't get here 58638995Sborman * until a response to the DO NAWS comes back) simulate the 58738995Sborman * receipt of a will echo. This will also send a WONT ECHO 58838995Sborman * to the client, since we assume that the client failed to 58938995Sborman * respond because it believes that it is already in DO ECHO 59038995Sborman * mode, which we do not want. 59138995Sborman */ 592*44363Sborman if (his_want_state_is_will(TELOPT_ECHO)) { 593*44363Sborman #ifdef DIAGNOSTICS 594*44363Sborman if (diagnostic & TD_OPTIONS) { 595*44363Sborman sprintf(nfrontp, "td: simulating recv\r\n"); 596*44363Sborman nfrontp += strlen(nfrontp); 597*44363Sborman } 598*44363Sborman #endif /* DIAGNOSTICS */ 59939503Sborman willoption(TELOPT_ECHO); 60040242Sborman } 60138995Sborman 60238995Sborman /* 60338995Sborman * Finally, to clean things up, we turn on our echo. This 60438995Sborman * will break stupid 4.2 telnets out of local terminal echo. 60538995Sborman */ 60638995Sborman 607*44363Sborman if (my_state_is_wont(TELOPT_ECHO)) 60839503Sborman send_will(TELOPT_ECHO, 1); 60938995Sborman 61038995Sborman /* 61138904Sborman * Turn on packet mode, and default to line at at time mode. 61238904Sborman */ 61338904Sborman (void) ioctl(p, TIOCPKT, (char *)&on); 61438904Sborman #ifdef LINEMODE 61538904Sborman tty_setlinemode(1); 61638904Sborman 61738904Sborman # ifdef KLUDGELINEMODE 61838904Sborman /* 61938904Sborman * Continuing line mode support. If client does not support 62038904Sborman * real linemode, attempt to negotiate kludge linemode by sending 62138904Sborman * the do timing mark sequence. 62238904Sborman */ 62338904Sborman if (lmodetype < REAL_LINEMODE) 62439503Sborman send_do(TELOPT_TM, 1); 62538904Sborman # endif /* KLUDGELINEMODE */ 62638904Sborman #endif /* LINEMODE */ 62738904Sborman 62838904Sborman /* 62938904Sborman * Call telrcv() once to pick up anything received during 63038904Sborman * terminal type negotiation, 4.2/4.3 determination, and 63138904Sborman * linemode negotiation. 63238904Sborman */ 63338904Sborman telrcv(); 63438904Sborman 63538904Sborman (void) ioctl(f, FIONBIO, (char *)&on); 63638904Sborman (void) ioctl(p, FIONBIO, (char *)&on); 63740242Sborman #if defined(CRAY2) && defined(UNICOS5) 63838904Sborman init_termdriver(f, p, interrupt, sendbrk); 63938904Sborman #endif 64038904Sborman 64138904Sborman #if defined(SO_OOBINLINE) 64238904Sborman (void) setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on); 64338904Sborman #endif /* defined(SO_OOBINLINE) */ 64438904Sborman 64538904Sborman #ifdef SIGTSTP 64638904Sborman (void) signal(SIGTSTP, SIG_IGN); 64738904Sborman #endif 64838904Sborman #ifdef SIGTTOU 64938904Sborman /* 65038904Sborman * Ignoring SIGTTOU keeps the kernel from blocking us 65138904Sborman * in ttioct() in /sys/tty.c. 65238904Sborman */ 65338904Sborman (void) signal(SIGTTOU, SIG_IGN); 65438904Sborman #endif 65538904Sborman 65638904Sborman (void) signal(SIGCHLD, cleanup); 65738904Sborman 65840242Sborman #if defined(CRAY2) && defined(UNICOS5) 65938904Sborman /* 66038904Sborman * Cray-2 will send a signal when pty modes are changed by slave 66138904Sborman * side. Set up signal handler now. 66238904Sborman */ 66338904Sborman if ((int)signal(SIGUSR1, termstat) < 0) 66438904Sborman perror("signal"); 66538904Sborman else if (ioctl(p, TCSIGME, (char *)SIGUSR1) < 0) 66638904Sborman perror("ioctl:TCSIGME"); 66738904Sborman /* 66838904Sborman * Make processing loop check terminal characteristics early on. 66938904Sborman */ 67038904Sborman termstat(); 67138904Sborman #endif 67238904Sborman 673*44363Sborman #ifdef NO_SETSID 67438904Sborman (void) setpgrp(0, 0); 675*44363Sborman #else 676*44363Sborman (void) setsid(); 67740242Sborman #endif 678*44363Sborman #if defined(TIOCSCTTY) && defined(CRAY) 679*44363Sborman ioctl(p, TIOCSCTTY, 0); 680*44363Sborman #endif 68138904Sborman 68238904Sborman /* 68312713Ssam * Show banner that getty never gave. 68427797Sminshall * 68533271Sminshall * We put the banner in the pty input buffer. This way, it 68633271Sminshall * gets carriage return null processing, etc., just like all 68733271Sminshall * other pty --> client data. 68812713Ssam */ 68927797Sminshall 69038904Sborman (void) gethostname(hostname, sizeof (hostname)); 69138904Sborman 69233271Sminshall if (getent(defent, "default") == 1) { 69333271Sminshall char *getstr(); 69438904Sborman char *cp=defstrs; 69527649Sminshall 69638904Sborman HE = getstr("he", &cp); 69738904Sborman HN = getstr("hn", &cp); 69838904Sborman IM = getstr("im", &cp); 69933271Sminshall if (HN && *HN) 70038904Sborman (void) strcpy(hostname, HN); 70138904Sborman if (IM == 0) 70238904Sborman IM = ""; 70333271Sminshall } else { 70438904Sborman #ifdef CRAY 70538904Sborman if (hostinfo == 0) 70638904Sborman IM = 0; 70738904Sborman else 70838904Sborman #endif 70938904Sborman IM = DEFAULT_IM; 71038904Sborman HE = 0; 71133271Sminshall } 71238904Sborman edithost(HE, hostname); 71338904Sborman if (IM && *IM) 71438904Sborman putf(IM, ptyibuf2); 71527797Sminshall 71638904Sborman if (pcc) 71738904Sborman (void) strncat(ptyibuf2, ptyip, pcc+1); 71838904Sborman ptyip = ptyibuf2; 71938904Sborman pcc = strlen(ptyip); 72040242Sborman #ifdef LINEMODE 72140242Sborman /* 72240242Sborman * Last check to make sure all our states are correct. 72340242Sborman */ 72440242Sborman init_termbuf(); 72540242Sborman localstat(); 72640242Sborman #endif /* LINEMODE */ 72733271Sminshall 728*44363Sborman #ifdef DIAGNOSTICS 729*44363Sborman if (diagnostic & TD_REPORT) { 730*44363Sborman sprintf(nfrontp, "td: Entering processing loop\r\n"); 731*44363Sborman nfrontp += strlen(nfrontp); 732*44363Sborman } 733*44363Sborman #endif /* DIAGNOSTICS */ 734*44363Sborman 7356002Sroot for (;;) { 73627185Sminshall fd_set ibits, obits, xbits; 7376002Sroot register int c; 7386002Sroot 73927185Sminshall if (ncc < 0 && pcc < 0) 74027185Sminshall break; 74127185Sminshall 74240242Sborman #if defined(CRAY2) && defined(UNICOS5) 74338904Sborman if (needtermstat) 74438904Sborman _termstat(); 74540242Sborman #endif /* defined(CRAY2) && defined(UNICOS5) */ 74627185Sminshall FD_ZERO(&ibits); 74727185Sminshall FD_ZERO(&obits); 74827185Sminshall FD_ZERO(&xbits); 7496002Sroot /* 7506002Sroot * Never look for input if there's still 7516002Sroot * stuff in the corresponding output buffer 7526002Sroot */ 75327185Sminshall if (nfrontp - nbackp || pcc > 0) { 75427185Sminshall FD_SET(f, &obits); 75527185Sminshall } else { 75627185Sminshall FD_SET(p, &ibits); 75727185Sminshall } 75827185Sminshall if (pfrontp - pbackp || ncc > 0) { 75927185Sminshall FD_SET(p, &obits); 76027185Sminshall } else { 76127185Sminshall FD_SET(f, &ibits); 76227185Sminshall } 76327185Sminshall if (!SYNCHing) { 76427185Sminshall FD_SET(f, &xbits); 76527185Sminshall } 76627185Sminshall if ((c = select(16, &ibits, &obits, &xbits, 76727185Sminshall (struct timeval *)0)) < 1) { 76827185Sminshall if (c == -1) { 76927185Sminshall if (errno == EINTR) { 77027185Sminshall continue; 77127185Sminshall } 77227185Sminshall } 7736002Sroot sleep(5); 7746002Sroot continue; 7756002Sroot } 7766002Sroot 7776002Sroot /* 77827185Sminshall * Any urgent data? 77927185Sminshall */ 78027185Sminshall if (FD_ISSET(net, &xbits)) { 78127185Sminshall SYNCHing = 1; 78227185Sminshall } 78327185Sminshall 78427185Sminshall /* 7856002Sroot * Something to read from the network... 7866002Sroot */ 78727185Sminshall if (FD_ISSET(net, &ibits)) { 78827649Sminshall #if !defined(SO_OOBINLINE) 78927185Sminshall /* 79027898Skarels * In 4.2 (and 4.3 beta) systems, the 79127185Sminshall * OOB indication and data handling in the kernel 79227185Sminshall * is such that if two separate TCP Urgent requests 79327185Sminshall * come in, one byte of TCP data will be overlaid. 79427185Sminshall * This is fatal for Telnet, but we try to live 79527185Sminshall * with it. 79627185Sminshall * 79727185Sminshall * In addition, in 4.2 (and...), a special protocol 79827185Sminshall * is needed to pick up the TCP Urgent data in 79927185Sminshall * the correct sequence. 80027185Sminshall * 80127185Sminshall * What we do is: if we think we are in urgent 80227185Sminshall * mode, we look to see if we are "at the mark". 80327185Sminshall * If we are, we do an OOB receive. If we run 80427185Sminshall * this twice, we will do the OOB receive twice, 80527185Sminshall * but the second will fail, since the second 80627185Sminshall * time we were "at the mark", but there wasn't 80727185Sminshall * any data there (the kernel doesn't reset 80827185Sminshall * "at the mark" until we do a normal read). 80927185Sminshall * Once we've read the OOB data, we go ahead 81027185Sminshall * and do normal reads. 81127185Sminshall * 81227185Sminshall * There is also another problem, which is that 81327185Sminshall * since the OOB byte we read doesn't put us 81427185Sminshall * out of OOB state, and since that byte is most 81527185Sminshall * likely the TELNET DM (data mark), we would 81627185Sminshall * stay in the TELNET SYNCH (SYNCHing) state. 81727185Sminshall * So, clocks to the rescue. If we've "just" 81827185Sminshall * received a DM, then we test for the 81927185Sminshall * presence of OOB data when the receive OOB 82027185Sminshall * fails (and AFTER we did the normal mode read 82127185Sminshall * to clear "at the mark"). 82227185Sminshall */ 82327185Sminshall if (SYNCHing) { 82427185Sminshall int atmark; 82527185Sminshall 82638904Sborman (void) ioctl(net, SIOCATMARK, (char *)&atmark); 82727185Sminshall if (atmark) { 82827185Sminshall ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB); 82927185Sminshall if ((ncc == -1) && (errno == EINVAL)) { 83027185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 83127983Sminshall if (sequenceIs(didnetreceive, gotDM)) { 83227185Sminshall SYNCHing = stilloob(net); 83327185Sminshall } 83427185Sminshall } 83527185Sminshall } else { 83627185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 8376002Sroot } 83827185Sminshall } else { 83927185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 84027185Sminshall } 84127185Sminshall settimer(didnetreceive); 84227649Sminshall #else /* !defined(SO_OOBINLINE)) */ 84327185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 84427649Sminshall #endif /* !defined(SO_OOBINLINE)) */ 84527185Sminshall if (ncc < 0 && errno == EWOULDBLOCK) 84627185Sminshall ncc = 0; 84727185Sminshall else { 84827185Sminshall if (ncc <= 0) { 84927185Sminshall break; 85027185Sminshall } 85127185Sminshall netip = netibuf; 85227185Sminshall } 853*44363Sborman #ifdef DIAGNOSTICS 854*44363Sborman if (diagnostic & (TD_REPORT | TD_NETDATA)) { 855*44363Sborman sprintf(nfrontp, "td: netread %d chars\r\n", ncc); 856*44363Sborman nfrontp += strlen(nfrontp); 857*44363Sborman } 858*44363Sborman if (diagnostic & TD_NETDATA) { 859*44363Sborman printdata("nd", netip, ncc); 860*44363Sborman } 861*44363Sborman #endif /* DIAGNOSTICS */ 8626002Sroot } 8636002Sroot 8646002Sroot /* 8656002Sroot * Something to read from the pty... 8666002Sroot */ 86738904Sborman if (FD_ISSET(p, &ibits)) { 8686002Sroot pcc = read(p, ptyibuf, BUFSIZ); 8696002Sroot if (pcc < 0 && errno == EWOULDBLOCK) 8706002Sroot pcc = 0; 8716002Sroot else { 8726002Sroot if (pcc <= 0) 8736002Sroot break; 87440242Sborman #if !defined(CRAY2) || !defined(UNICOS5) 87538904Sborman #ifdef LINEMODE 87638904Sborman /* 87738904Sborman * If ioctl from pty, pass it through net 87838904Sborman */ 87938904Sborman if (ptyibuf[0] & TIOCPKT_IOCTL) { 88038904Sborman copy_termbuf(ptyibuf+1, pcc-1); 88138904Sborman localstat(); 88238904Sborman pcc = 1; 88338904Sborman } 88438904Sborman #endif LINEMODE 88537210Sminshall if (ptyibuf[0] & TIOCPKT_FLUSHWRITE) { 88638904Sborman netclear(); /* clear buffer back */ 88740242Sborman #ifdef notdef 88840242Sborman /* 88940242Sborman * We really should have this in, but 89040242Sborman * there are client telnets on some 89140242Sborman * operating systems get screwed up 89240242Sborman * royally if we send them urgent 89340242Sborman * mode data. So, for now, we'll not 89440242Sborman * do this... 89540242Sborman */ 89637210Sminshall *nfrontp++ = IAC; 89737210Sminshall *nfrontp++ = DM; 89837210Sminshall neturg = nfrontp-1; /* off by one XXX */ 89940242Sborman #endif 90037210Sminshall } 901*44363Sborman if (his_state_is_will(TELOPT_LFLOW) && 90237210Sminshall (ptyibuf[0] & 90338904Sborman (TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))) { 90438904Sborman (void) sprintf(nfrontp, "%c%c%c%c%c%c", 90537210Sminshall IAC, SB, TELOPT_LFLOW, 90637210Sminshall ptyibuf[0] & TIOCPKT_DOSTOP ? 1 : 0, 90737210Sminshall IAC, SE); 90837210Sminshall nfrontp += 6; 90937210Sminshall } 91033267Sminshall pcc--; 91133267Sminshall ptyip = ptyibuf+1; 91240242Sborman #else /* defined(CRAY2) && defined(UNICOS5) */ 91338904Sborman if (!uselinemode) { 91439531Sborman unpcc = pcc; 91539531Sborman unptyip = ptyibuf; 91639531Sborman pcc = term_output(&unptyip, ptyibuf2, 91739531Sborman &unpcc, BUFSIZ); 91838904Sborman ptyip = ptyibuf2; 91938904Sborman } else 92038904Sborman ptyip = ptyibuf; 92140242Sborman #endif /* defined(CRAY2) && defined(UNICOS5) */ 92238904Sborman } 9236002Sroot } 9246002Sroot 9256002Sroot while (pcc > 0) { 9266002Sroot if ((&netobuf[BUFSIZ] - nfrontp) < 2) 9276002Sroot break; 9286002Sroot c = *ptyip++ & 0377, pcc--; 9296002Sroot if (c == IAC) 9306002Sroot *nfrontp++ = c; 93140242Sborman #if defined(CRAY2) && defined(UNICOS5) 93238904Sborman else if (c == '\n' && 933*44363Sborman my_state_is_wont(TELOPT_BINARY) && newmap) 93438904Sborman *nfrontp++ = '\r'; 93540242Sborman #endif /* defined(CRAY2) && defined(UNICOS5) */ 9366002Sroot *nfrontp++ = c; 937*44363Sborman if ((c == '\r') && (my_state_is_wont(TELOPT_BINARY))) { 93827020Sminshall if (pcc > 0 && ((*ptyip & 0377) == '\n')) { 93927020Sminshall *nfrontp++ = *ptyip++ & 0377; 94027020Sminshall pcc--; 94127020Sminshall } else 94227020Sminshall *nfrontp++ = '\0'; 94327020Sminshall } 9446002Sroot } 94540242Sborman #if defined(CRAY2) && defined(UNICOS5) 94639531Sborman /* 94739531Sborman * If chars were left over from the terminal driver, 94839531Sborman * note their existence. 94939531Sborman */ 95039531Sborman if (!uselinemode && unpcc) { 95139531Sborman pcc = unpcc; 95239531Sborman unpcc = 0; 95339531Sborman ptyip = unptyip; 95439531Sborman } 95540242Sborman #endif /* defined(CRAY2) && defined(UNICOS5) */ 95639531Sborman 95727185Sminshall if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0) 9586002Sroot netflush(); 9596002Sroot if (ncc > 0) 9606002Sroot telrcv(); 96127185Sminshall if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0) 9626002Sroot ptyflush(); 9636002Sroot } 9646002Sroot cleanup(); 96538904Sborman } /* end of telnet */ 9666002Sroot 96738904Sborman #ifndef TCSIG 96838904Sborman # ifdef TIOCSIG 96938904Sborman # define TCSIG TIOCSIG 97038904Sborman # endif 97138904Sborman #endif 9726002Sroot 97337212Sminshall /* 9746002Sroot * Send interrupt to process on other side of pty. 9756002Sroot * If it is in raw mode, just write NULL; 9766002Sroot * otherwise, write intr char. 9776002Sroot */ 9786002Sroot interrupt() 9796002Sroot { 98038904Sborman ptyflush(); /* half-hearted */ 9816002Sroot 98238904Sborman #ifdef TCSIG 98338904Sborman (void) ioctl(pty, TCSIG, (char *)SIGINT); 98438904Sborman #else /* TCSIG */ 98538904Sborman init_termbuf(); 98640242Sborman *pfrontp++ = slctab[SLC_IP].sptr ? 98740242Sborman (unsigned char)*slctab[SLC_IP].sptr : '\177'; 98838904Sborman #endif /* TCSIG */ 9896002Sroot } 9906002Sroot 99127229Sminshall /* 99227229Sminshall * Send quit to process on other side of pty. 99327229Sminshall * If it is in raw mode, just write NULL; 99427229Sminshall * otherwise, write quit char. 99527229Sminshall */ 99627229Sminshall sendbrk() 99727229Sminshall { 99827229Sminshall ptyflush(); /* half-hearted */ 99938904Sborman #ifdef TCSIG 100038904Sborman (void) ioctl(pty, TCSIG, (char *)SIGQUIT); 100138904Sborman #else /* TCSIG */ 100238904Sborman init_termbuf(); 100340242Sborman *pfrontp++ = slctab[SLC_ABORT].sptr ? 100440242Sborman (unsigned char)*slctab[SLC_ABORT].sptr : '\034'; 100538904Sborman #endif /* TCSIG */ 100627229Sminshall } 100727229Sminshall 100838904Sborman sendsusp() 10096002Sroot { 101038904Sborman #ifdef SIGTSTP 101138904Sborman ptyflush(); /* half-hearted */ 101238904Sborman # ifdef TCSIG 101338904Sborman (void) ioctl(pty, TCSIG, (char *)SIGTSTP); 101438904Sborman # else /* TCSIG */ 101540242Sborman *pfrontp++ = slctab[SLC_SUSP].sptr ? 101640242Sborman (unsigned char)*slctab[SLC_SUSP].sptr : '\032'; 101738904Sborman # endif /* TCSIG */ 101838904Sborman #endif /* SIGTSTP */ 10196002Sroot } 10206002Sroot 102138904Sborman doeof() 10226002Sroot { 102340242Sborman #if defined(USE_TERMIO) && defined(SYSV_TERMIO) 102440242Sborman extern char oldeofc; 102540242Sborman #endif 102638904Sborman init_termbuf(); 10276002Sroot 102840242Sborman #if defined(USE_TERMIO) && defined(SYSV_TERMIO) 102940242Sborman if (!tty_isediting()) { 103040242Sborman *pfrontp++ = oldeofc; 103140242Sborman return; 103240242Sborman } 103340242Sborman #endif 103440242Sborman *pfrontp++ = slctab[SLC_EOF].sptr ? 103540242Sborman (unsigned char)*slctab[SLC_EOF].sptr : '\004'; 10366002Sroot } 1037