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*45234Sborman static char sccsid[] = "@(#)telnetd.c 5.47 (Berkeley) 09/14/90"; 1633687Sbostic #endif /* not lint */ 1721182Sdist 1838904Sborman #include "telnetd.h" 19*45234Sborman #include "pathnames.h" 2038904Sborman 216002Sroot /* 2238904Sborman * I/O data buffers, 2338904Sborman * pointers, and counters. 246002Sroot */ 2538904Sborman char ptyibuf[BUFSIZ], *ptyip = ptyibuf; 2638904Sborman char ptyibuf2[BUFSIZ]; 279218Ssam 2838904Sborman int hostinfo = 1; /* do we print login banner? */ 299218Ssam 3038904Sborman #ifdef CRAY 3138904Sborman extern int newmap; /* nonzero if \n maps to ^M^J */ 3240242Sborman int lowpty = 0, highpty; /* low, high pty numbers */ 3338904Sborman #endif /* CRAY */ 3412216Ssam 3538904Sborman int debug = 0; 3638904Sborman char *progname; 379218Ssam 3844363Sborman #if defined(NEED_GETTOS) 3944363Sborman struct tosent { 4044363Sborman char *t_name; /* name */ 4144363Sborman char **t_aliases; /* alias list */ 4244363Sborman char *t_proto; /* protocol */ 4344363Sborman int t_tos; /* Type Of Service bits */ 4444363Sborman }; 4544363Sborman 4644363Sborman struct tosent * 4744363Sborman gettosbyname(name, proto) 4844363Sborman char *name, *proto; 4944363Sborman { 5044363Sborman static struct tosent te; 5144363Sborman static char *aliasp = 0; 5244363Sborman 5344363Sborman te.t_name = name; 5444363Sborman te.t_aliases = &aliasp; 5544363Sborman te.t_proto = proto; 5644363Sborman te.t_tos = 020; /* Low Delay bit */ 5744363Sborman return(&te); 5844363Sborman } 5944363Sborman #endif 6044363Sborman 6138904Sborman main(argc, argv) 6238904Sborman char *argv[]; 6338904Sborman { 6438904Sborman struct sockaddr_in from; 6538904Sborman int on = 1, fromlen; 6644363Sborman #if defined(HAS_IP_TOS) || defined(NEED_GETTOS) 6740242Sborman struct tosent *tp; 6844363Sborman #endif /* defined(HAS_IP_TOS) || defined(NEED_GETTOS) */ 696002Sroot 7038904Sborman pfrontp = pbackp = ptyobuf; 7138904Sborman netip = netibuf; 7238904Sborman nfrontp = nbackp = netobuf; 736002Sroot 7438904Sborman progname = *argv; 7540242Sborman 7640242Sborman #ifdef CRAY 7740242Sborman /* 7840242Sborman * Get number of pty's before trying to process options, 7940242Sborman * which may include changing pty range. 8040242Sborman */ 8140242Sborman highpty = getnpty(); 8240242Sborman #endif /* CRAY */ 8340242Sborman 8438904Sborman top: 8538904Sborman argc--, argv++; 86*45234Sborman #ifdef convex 87*45234Sborman if (argc == 1 && !debug) 88*45234Sborman argc--; /* ignore the host/port name */ 89*45234Sborman #endif 9027649Sminshall 9138904Sborman if (argc > 0 && strcmp(*argv, "-debug") == 0) { 9238904Sborman debug++; 9338904Sborman goto top; 9438904Sborman } 9527649Sminshall 9638904Sborman #ifdef LINEMODE 9738904Sborman if (argc > 0 && !strcmp(*argv, "-l")) { 9838904Sborman alwayslinemode = 1; 9938904Sborman goto top; 10038904Sborman } 10138904Sborman #endif /* LINEMODE */ 10227649Sminshall 10338904Sborman if (argc > 0 && !strcmp(*argv, "-h")) { 10438904Sborman hostinfo = 0; 10538904Sborman goto top; 10638904Sborman } 10727649Sminshall 108*45234Sborman #ifdef CRAY 10938904Sborman if (argc > 0 && !strncmp(*argv, "-r", 2)) { 11038904Sborman char *strchr(); 11138904Sborman char *c; 11227649Sminshall 11340242Sborman /* 11440242Sborman * Allow the specification of alterations to the pty search 11540242Sborman * range. It is legal to specify only one, and not change the 11640242Sborman * other from its default. 11740242Sborman */ 11838904Sborman *argv += 2; 11938904Sborman if (**argv == '\0' && argc) 12038904Sborman argv++, argc--; 12138904Sborman c = strchr(*argv, '-'); 12238904Sborman if (c) { 12338904Sborman *c++ = '\0'; 12438904Sborman highpty = atoi(c); 12540242Sborman } 12640242Sborman if (**argv != '\0') 12740242Sborman lowpty = atoi(*argv); 12840242Sborman if ((lowpty > highpty) || (lowpty < 0) || (highpty > 32767)) { 12944363Sborman usage(); 13044363Sborman /* NOT REACHED */ 13138904Sborman } 13238904Sborman goto top; 13338904Sborman } 13438904Sborman # ifdef NEWINIT 13538904Sborman if (argc > 0 && !strncmp(*argv, "-I", 2)) { 13638904Sborman extern char *gen_id; 13727649Sminshall 13838904Sborman *argv += 2; 13938904Sborman if (**argv == '\0') { 14044363Sborman if (argc < 2) { 14144363Sborman usage(); 14244363Sborman /* NOT REACHED */ 14344363Sborman } 14438904Sborman argv++, argc--; 14544363Sborman if (**argv == '\0') { 14644363Sborman usage(); 14744363Sborman /* NOT REACHED */ 14844363Sborman } 14938904Sborman } 15038904Sborman gen_id = *argv; 15138904Sborman goto top; 15238904Sborman } 15338904Sborman # endif /* NEWINIT */ 15438904Sborman #endif /* CRAY */ 1556002Sroot 15644363Sborman #ifdef DIAGNOSTICS 15744363Sborman /* 15844363Sborman * Check for desired diagnostics capabilities. 15944363Sborman */ 16044363Sborman if (argc > 0 && !strncmp(*argv, "-D", 2)) { 16144363Sborman *argv += 2; 16244363Sborman if (**argv == '\0') { 16344363Sborman if (argc < 2) { 16444363Sborman usage(); 16544363Sborman /* NOT REACHED */ 16644363Sborman } 16744363Sborman argv++, argc--; 16844363Sborman if (**argv == '\0') { 16944363Sborman usage(); 17044363Sborman /* NOT REACHED */ 17144363Sborman } 17244363Sborman } 17344363Sborman if (!strcmp(*argv, "report")) { 17444363Sborman diagnostic |= TD_REPORT|TD_OPTIONS; 17544363Sborman } else if (!strcmp(*argv, "exercise")) { 17644363Sborman diagnostic |= TD_EXERCISE; 17744363Sborman } else if (!strcmp(*argv, "netdata")) { 17844363Sborman diagnostic |= TD_NETDATA; 17944363Sborman } else if (!strcmp(*argv, "ptydata")) { 18044363Sborman diagnostic |= TD_PTYDATA; 18144363Sborman } else if (!strcmp(*argv, "options")) { 18244363Sborman diagnostic |= TD_OPTIONS; 18344363Sborman } else { 18444363Sborman usage(); 18544363Sborman /* NOT REACHED */ 18644363Sborman } 18744363Sborman goto top; 18844363Sborman } 18944363Sborman #endif /* DIAGNOSTICS */ 19044363Sborman 19144363Sborman #ifdef BFTPDAEMON 19244363Sborman /* 19344363Sborman * Check for bftp daemon 19444363Sborman */ 19544363Sborman if (argc > 0 && !strncmp(*argv, "-B", 2)) { 19644363Sborman bftpd++; 19744363Sborman goto top; 19844363Sborman } 19944363Sborman #endif /* BFTPDAEMON */ 20044363Sborman 20144363Sborman if (argc > 0 && **argv == '-') { 20244363Sborman fprintf(stderr, "telnetd: %s: unknown option\n", *argv+1); 20344363Sborman usage(); 20444363Sborman /* NOT REACHED */ 20544363Sborman } 20644363Sborman 20738904Sborman if (debug) { 20827185Sminshall int s, ns, foo; 20927185Sminshall struct servent *sp; 21027185Sminshall static struct sockaddr_in sin = { AF_INET }; 21127185Sminshall 21244363Sborman if (argc > 1) { 21344363Sborman usage(); 21444363Sborman /* NOT REACHED */ 21544363Sborman } else if (argc == 1) { 21638904Sborman if (sp = getservbyname(*argv, "tcp")) { 21738904Sborman sin.sin_port = sp->s_port; 21838904Sborman } else { 21938904Sborman sin.sin_port = atoi(*argv); 22038904Sborman if ((int)sin.sin_port <= 0) { 22138904Sborman fprintf(stderr, "telnetd: %s: bad port #\n", *argv); 22244363Sborman usage(); 22344363Sborman /* NOT REACHED */ 22438904Sborman } 22538904Sborman sin.sin_port = htons((u_short)sin.sin_port); 22638904Sborman } 22737210Sminshall } else { 22837210Sminshall sp = getservbyname("telnet", "tcp"); 22937210Sminshall if (sp == 0) { 23044363Sborman fprintf(stderr, "telnetd: tcp/telnet: unknown service\n"); 23138904Sborman exit(1); 23237210Sminshall } 23337210Sminshall sin.sin_port = sp->s_port; 23427185Sminshall } 23527185Sminshall 23627185Sminshall s = socket(AF_INET, SOCK_STREAM, 0); 23727185Sminshall if (s < 0) { 23827185Sminshall perror("telnetd: socket");; 23927185Sminshall exit(1); 24027185Sminshall } 24138904Sborman (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); 24238904Sborman if (bind(s, (struct sockaddr *)&sin, sizeof sin) < 0) { 24327185Sminshall perror("bind"); 24427185Sminshall exit(1); 24527185Sminshall } 24627185Sminshall if (listen(s, 1) < 0) { 24727185Sminshall perror("listen"); 24827185Sminshall exit(1); 24927185Sminshall } 25027185Sminshall foo = sizeof sin; 25138904Sborman ns = accept(s, (struct sockaddr *)&sin, &foo); 25227185Sminshall if (ns < 0) { 25327185Sminshall perror("accept"); 25427185Sminshall exit(1); 25527185Sminshall } 25638904Sborman (void) dup2(ns, 0); 25738904Sborman (void) close(ns); 25838904Sborman (void) close(s); 25944363Sborman } else if (argc > 0) { 26044363Sborman usage(); 26144363Sborman /* NOT REACHED */ 26227185Sminshall } 26338904Sborman 26424855Seric openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON); 26516371Skarels fromlen = sizeof (from); 26638904Sborman if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) { 26738904Sborman fprintf(stderr, "%s: ", progname); 26816371Skarels perror("getpeername"); 26916371Skarels _exit(1); 2708346Ssam } 27117156Ssam if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) { 27217187Sralph syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); 27310418Ssam } 27440242Sborman 27544363Sborman #if defined(HAS_IP_TOS) || defined(NEED_GETTOS) 27644363Sborman if ((tp = gettosbyname("telnet", "tcp")) && 27744363Sborman (setsockopt(0, IPPROTO_IP, IP_TOS, &tp->t_tos, sizeof(int)) < 0)) 27840242Sborman syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); 27944363Sborman #endif /* defined(HAS_IP_TOS) || defined(NEED_GETTOS) */ 28038904Sborman net = 0; 28138904Sborman doit(&from); 28238904Sborman /* NOTREACHED */ 28338904Sborman } /* end of main */ 2846002Sroot 28544363Sborman usage() 28644363Sborman { 28744363Sborman fprintf(stderr, "Usage: telnetd [-debug] [-h]"); 28844363Sborman #ifdef NEWINIT 28944363Sborman fprintf(stderr, " [-Iinitid]"); 29044363Sborman #endif /* NEWINIT */ 29144363Sborman #ifdef DIAGNOSTICS 29244363Sborman fprintf(stderr, " [-D (options|report|exercise|netdata|ptydata)]"); 29344363Sborman #endif /* DIAGNOSTICS */ 29444363Sborman #ifdef LINEMODE 29544363Sborman fprintf(stderr, " [-l]"); 29644363Sborman #endif 29744363Sborman #ifdef CRAY 29844363Sborman fprintf(stderr, " [-r[lowpty]-[highpty]]"); 29944363Sborman #endif 30044363Sborman #ifdef BFTPDAEMON 30144363Sborman fprintf(stderr, " [-B]"); 30244363Sborman #endif /* BFTPDAEMON */ 30344363Sborman fprintf(stderr, " [port]\n"); 30444363Sborman exit(1); 30544363Sborman } 30644363Sborman 30740242Sborman void cleanup(); 30827649Sminshall 30927649Sminshall /* 31027983Sminshall * getterminaltype 31127649Sminshall * 31238904Sborman * Ask the other end to send along its terminal type and speed. 31327983Sminshall * Output is the variable terminaltype filled in. 31427649Sminshall */ 31538904Sborman static char ttytype_sbbuf[] = { IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE }; 31627983Sminshall void 31727983Sminshall getterminaltype() 31827649Sminshall { 31938904Sborman void ttloop(); 32027649Sminshall 32138904Sborman settimer(baseline); 32239503Sborman send_do(TELOPT_TTYPE, 1); 32339503Sborman send_do(TELOPT_TSPEED, 1); 32444363Sborman send_do(TELOPT_XDISPLOC, 1); 32544363Sborman send_do(TELOPT_ENVIRON, 1); 32644363Sborman while (his_will_wont_is_changing(TELOPT_TTYPE) || 32744363Sborman his_will_wont_is_changing(TELOPT_TSPEED) || 32844363Sborman his_will_wont_is_changing(TELOPT_XDISPLOC) || 32944363Sborman his_will_wont_is_changing(TELOPT_ENVIRON)) { 33027983Sminshall ttloop(); 33127649Sminshall } 33244363Sborman if (his_state_is_will(TELOPT_TSPEED)) { 33338904Sborman static char sbbuf[] = { IAC, SB, TELOPT_TSPEED, TELQUAL_SEND, IAC, SE }; 33427983Sminshall 33527983Sminshall bcopy(sbbuf, nfrontp, sizeof sbbuf); 33627983Sminshall nfrontp += sizeof sbbuf; 33738904Sborman } 33844363Sborman if (his_state_is_will(TELOPT_XDISPLOC)) { 33944363Sborman static char sbbuf[] = { IAC, SB, TELOPT_XDISPLOC, TELQUAL_SEND, IAC, SE }; 34038904Sborman 34144363Sborman bcopy(sbbuf, nfrontp, sizeof sbbuf); 34244363Sborman nfrontp += sizeof sbbuf; 34344363Sborman } 34444363Sborman if (his_state_is_will(TELOPT_ENVIRON)) { 34544363Sborman static char sbbuf[] = { IAC, SB, TELOPT_ENVIRON, TELQUAL_SEND, IAC, SE }; 34644363Sborman 34744363Sborman bcopy(sbbuf, nfrontp, sizeof sbbuf); 34844363Sborman nfrontp += sizeof sbbuf; 34944363Sborman } 35044363Sborman if (his_state_is_will(TELOPT_TTYPE)) { 35144363Sborman 35238904Sborman bcopy(ttytype_sbbuf, nfrontp, sizeof ttytype_sbbuf); 35338904Sborman nfrontp += sizeof ttytype_sbbuf; 35438904Sborman } 35544363Sborman if (his_state_is_will(TELOPT_TSPEED)) { 35638904Sborman while (sequenceIs(tspeedsubopt, baseline)) 35727983Sminshall ttloop(); 35838904Sborman } 35944363Sborman if (his_state_is_will(TELOPT_XDISPLOC)) { 36044363Sborman while (sequenceIs(xdisplocsubopt, baseline)) 36144363Sborman ttloop(); 36244363Sborman } 36344363Sborman if (his_state_is_will(TELOPT_ENVIRON)) { 36444363Sborman while (sequenceIs(environsubopt, baseline)) 36544363Sborman ttloop(); 36644363Sborman } 36744363Sborman if (his_state_is_will(TELOPT_TTYPE)) { 36838904Sborman char first[256], last[256]; 36938904Sborman 37038904Sborman while (sequenceIs(ttypesubopt, baseline)) 37138904Sborman ttloop(); 37238904Sborman 37344363Sborman /* 37444363Sborman * If the other side has already disabled the option, then 37544363Sborman * we have to just go with what we (might) have already gotten. 37644363Sborman */ 37744363Sborman if (his_state_is_will(TELOPT_TTYPE) && !terminaltypeok(terminaltype)) { 37838904Sborman (void) strncpy(first, terminaltype, sizeof(first)); 37938904Sborman for(;;) { 38038904Sborman /* 38138904Sborman * Save the unknown name, and request the next name. 38238904Sborman */ 38338904Sborman (void) strncpy(last, terminaltype, sizeof(last)); 38438904Sborman _gettermname(); 38544363Sborman if (terminaltypeok(terminaltype)) 38638904Sborman break; 38744363Sborman if ((strncmp(last, terminaltype, sizeof(last)) == 0) || 38844363Sborman his_state_is_wont(TELOPT_TTYPE)) { 38938904Sborman /* 39038904Sborman * We've hit the end. If this is the same as 39138904Sborman * the first name, just go with it. 39238904Sborman */ 393*45234Sborman if (strncmp(first, terminaltype, sizeof(first)) == 0) 39438904Sborman break; 39538904Sborman /* 39644363Sborman * Get the terminal name one more time, so that 39738904Sborman * RFC1091 compliant telnets will cycle back to 39838904Sborman * the start of the list. 39938904Sborman */ 40044363Sborman _gettermname(); 401*45234Sborman if (strncmp(first, terminaltype, sizeof(first)) != 0) 40238904Sborman (void) strncpy(terminaltype, first, sizeof(first)); 40338904Sborman break; 40438904Sborman } 40538904Sborman } 40627983Sminshall } 40727983Sminshall } 40838904Sborman } /* end of getterminaltype */ 40938904Sborman 41038904Sborman _gettermname() 41138904Sborman { 41244363Sborman /* 41344363Sborman * If the client turned off the option, 41444363Sborman * we can't send another request, so we 41544363Sborman * just return. 41644363Sborman */ 41744363Sborman if (his_state_is_wont(TELOPT_TTYPE)) 41844363Sborman return; 41938904Sborman settimer(baseline); 42038904Sborman bcopy(ttytype_sbbuf, nfrontp, sizeof ttytype_sbbuf); 42138904Sborman nfrontp += sizeof ttytype_sbbuf; 42238904Sborman while (sequenceIs(ttypesubopt, baseline)) 42338904Sborman ttloop(); 42427649Sminshall } 42527649Sminshall 42638904Sborman terminaltypeok(s) 42738904Sborman char *s; 42838904Sborman { 42938904Sborman char buf[1024]; 43038904Sborman 43138904Sborman if (terminaltype == NULL) 43238904Sborman return(1); 43338904Sborman 43438904Sborman /* 43538904Sborman * tgetent() will return 1 if the type is known, and 43638904Sborman * 0 if it is not known. If it returns -1, it couldn't 43738904Sborman * open the database. But if we can't open the database, 43838904Sborman * it won't help to say we failed, because we won't be 43938904Sborman * able to verify anything else. So, we treat -1 like 1. 44038904Sborman */ 44138904Sborman if (tgetent(buf, s) == 0) 44238904Sborman return(0); 44338904Sborman return(1); 44438904Sborman } 44538904Sborman 4466002Sroot /* 4476002Sroot * Get a pty, scan input lines. 4486002Sroot */ 44938904Sborman doit(who) 45012683Ssam struct sockaddr_in *who; 4516002Sroot { 45220188Skarels char *host, *inet_ntoa(); 45338904Sborman int t; 45412683Ssam struct hostent *hp; 4556002Sroot 45638904Sborman /* 45738904Sborman * Find an available pty to use. 45838904Sborman */ 459*45234Sborman #ifndef convex 46038904Sborman pty = getpty(); 46138904Sborman if (pty < 0) 46238904Sborman fatal(net, "All network ports in use"); 463*45234Sborman #else 464*45234Sborman for (;;) { 465*45234Sborman char *lp; 466*45234Sborman extern char *line, *getpty(); 46720188Skarels 468*45234Sborman if ((lp = getpty()) == NULL) 469*45234Sborman fatal(net, "Out of ptys"); 470*45234Sborman 471*45234Sborman if ((pty = open(lp, 2)) >= 0) { 472*45234Sborman strcpy(line,lp); 473*45234Sborman line[5] = 't'; 474*45234Sborman break; 475*45234Sborman } 476*45234Sborman } 47744545Smarc #endif 47838904Sborman 47938904Sborman /* get name of connected client */ 48038904Sborman hp = gethostbyaddr((char *)&who->sin_addr, sizeof (struct in_addr), 48112683Ssam who->sin_family); 48212683Ssam if (hp) 48312683Ssam host = hp->h_name; 48412683Ssam else 48517444Sralph host = inet_ntoa(who->sin_addr); 48627983Sminshall 48744363Sborman init_env(); 48827983Sminshall /* 48938904Sborman * get terminal type. 49027983Sminshall */ 49127983Sminshall getterminaltype(); 49244363Sborman setenv("TERM", terminaltype ? terminaltype : "network", 1); 49327983Sminshall 49427649Sminshall /* 49538904Sborman * Start up the login process on the slave side of the terminal 49627649Sminshall */ 497*45234Sborman #ifndef convex 498*45234Sborman startslave(host); 49938904Sborman 50038904Sborman telnet(net, pty); /* begin server processing */ 501*45234Sborman #else 502*45234Sborman telnet(net, pty, host); 503*45234Sborman #endif 5049244Ssam /*NOTREACHED*/ 50538904Sborman } /* end of doit */ 5069244Ssam 50738904Sborman #ifndef MAXHOSTNAMELEN 50838904Sborman #define MAXHOSTNAMELEN 64 50938904Sborman #endif MAXHOSTNAMELEN 5106002Sroot /* 5116002Sroot * Main loop. Select from pty and network, and 5126002Sroot * hand data to telnet receiver finite state machine. 5136002Sroot */ 514*45234Sborman #ifndef convex 5156002Sroot telnet(f, p) 516*45234Sborman #else 517*45234Sborman telnet(f, p, host) 518*45234Sborman #endif 51938904Sborman int f, p; 520*45234Sborman #ifdef convex 521*45234Sborman char *host; 522*45234Sborman #endif 5236002Sroot { 5246002Sroot int on = 1; 52527898Skarels char hostname[MAXHOSTNAMELEN]; 52640242Sborman #if defined(CRAY2) && defined(UNICOS5) 52738904Sborman int termstat(); 52838904Sborman int interrupt(), sendbrk(); 52938904Sborman #endif 53033271Sminshall #define TABBUFSIZ 512 53133271Sminshall char defent[TABBUFSIZ]; 53233271Sminshall char defstrs[TABBUFSIZ]; 53333271Sminshall #undef TABBUFSIZ 53433271Sminshall char *HE; 53533271Sminshall char *HN; 53633271Sminshall char *IM; 53738904Sborman void netflush(); 53838904Sborman 53932400Sminshall /* 54038904Sborman * Initialize the slc mapping table. 54132400Sminshall */ 54238904Sborman get_slc_defaults(); 5436002Sroot 5448379Ssam /* 54538904Sborman * Do some tests where it is desireable to wait for a response. 54638904Sborman * Rather than doing them slowly, one at a time, do them all 54738904Sborman * at once. 5488379Ssam */ 54944363Sborman if (my_state_is_wont(TELOPT_SGA)) 55039503Sborman send_will(TELOPT_SGA, 1); 55112713Ssam /* 55227649Sminshall * Is the client side a 4.2 (NOT 4.3) system? We need to know this 55327649Sminshall * because 4.2 clients are unable to deal with TCP urgent data. 55427649Sminshall * 55527649Sminshall * To find out, we send out a "DO ECHO". If the remote system 55627649Sminshall * answers "WILL ECHO" it is probably a 4.2 client, and we note 55727649Sminshall * that fact ("WILL ECHO" ==> that the client will echo what 55827649Sminshall * WE, the server, sends it; it does NOT mean that the client will 55927649Sminshall * echo the terminal input). 56027649Sminshall */ 56139503Sborman send_do(TELOPT_ECHO, 1); 56227649Sminshall 56338904Sborman #ifdef LINEMODE 56444363Sborman if (his_state_is_wont(TELOPT_LINEMODE)) { 56538904Sborman /* Query the peer for linemode support by trying to negotiate 56638904Sborman * the linemode option. 56738904Sborman */ 56844363Sborman linemode = 0; 56938904Sborman editmode = 0; 57039503Sborman send_do(TELOPT_LINEMODE, 1); /* send do linemode */ 57138904Sborman } 57238904Sborman #endif /* LINEMODE */ 57338904Sborman 57427649Sminshall /* 57538904Sborman * Send along a couple of other options that we wish to negotiate. 57638904Sborman */ 57739503Sborman send_do(TELOPT_NAWS, 1); 57839503Sborman send_will(TELOPT_STATUS, 1); 57938904Sborman flowmode = 1; /* default flow control state */ 58039503Sborman send_do(TELOPT_LFLOW, 1); 58138904Sborman 58238904Sborman /* 58338904Sborman * Spin, waiting for a response from the DO ECHO. However, 58438904Sborman * some REALLY DUMB telnets out there might not respond 58538904Sborman * to the DO ECHO. So, we spin looking for NAWS, (most dumb 58638904Sborman * telnets so far seem to respond with WONT for a DO that 58738904Sborman * they don't understand...) because by the time we get the 58838904Sborman * response, it will already have processed the DO ECHO. 58938904Sborman * Kludge upon kludge. 59038904Sborman */ 59144363Sborman while (his_will_wont_is_changing(TELOPT_NAWS)) 59238904Sborman ttloop(); 59338904Sborman 59438904Sborman /* 59544363Sborman * But... 59644363Sborman * The client might have sent a WILL NAWS as part of its 59744363Sborman * startup code; if so, we'll be here before we get the 59844363Sborman * response to the DO ECHO. We'll make the assumption 59944363Sborman * that any implementation that understands about NAWS 60044363Sborman * is a modern enough implementation that it will respond 60144363Sborman * to our DO ECHO request; hence we'll do another spin 60244363Sborman * waiting for the ECHO option to settle down, which is 60344363Sborman * what we wanted to do in the first place... 60444363Sborman */ 60544363Sborman if (his_want_state_is_will(TELOPT_ECHO) && 60644363Sborman his_state_is_will(TELOPT_NAWS)) { 60744363Sborman while (his_will_wont_is_changing(TELOPT_ECHO)) 60844363Sborman ttloop(); 60944363Sborman } 61044363Sborman /* 61138995Sborman * On the off chance that the telnet client is broken and does not 61238995Sborman * respond to the DO ECHO we sent, (after all, we did send the 61338995Sborman * DO NAWS negotiation after the DO ECHO, and we won't get here 61438995Sborman * until a response to the DO NAWS comes back) simulate the 61538995Sborman * receipt of a will echo. This will also send a WONT ECHO 61638995Sborman * to the client, since we assume that the client failed to 61738995Sborman * respond because it believes that it is already in DO ECHO 61838995Sborman * mode, which we do not want. 61938995Sborman */ 62044363Sborman if (his_want_state_is_will(TELOPT_ECHO)) { 62144363Sborman #ifdef DIAGNOSTICS 62244363Sborman if (diagnostic & TD_OPTIONS) { 62344363Sborman sprintf(nfrontp, "td: simulating recv\r\n"); 62444363Sborman nfrontp += strlen(nfrontp); 62544363Sborman } 62644363Sborman #endif /* DIAGNOSTICS */ 62739503Sborman willoption(TELOPT_ECHO); 62840242Sborman } 62938995Sborman 63038995Sborman /* 63138995Sborman * Finally, to clean things up, we turn on our echo. This 63238995Sborman * will break stupid 4.2 telnets out of local terminal echo. 63338995Sborman */ 63438995Sborman 63544363Sborman if (my_state_is_wont(TELOPT_ECHO)) 63639503Sborman send_will(TELOPT_ECHO, 1); 63738995Sborman 63838995Sborman /* 639*45234Sborman * Turn on packet mode 64038904Sborman */ 64138904Sborman (void) ioctl(p, TIOCPKT, (char *)&on); 642*45234Sborman #ifdef KLUDGELINEMODE 64338904Sborman /* 64438904Sborman * Continuing line mode support. If client does not support 64538904Sborman * real linemode, attempt to negotiate kludge linemode by sending 64638904Sborman * the do timing mark sequence. 64738904Sborman */ 64838904Sborman if (lmodetype < REAL_LINEMODE) 64939503Sborman send_do(TELOPT_TM, 1); 650*45234Sborman #endif /* KLUDGELINEMODE */ 65138904Sborman 65238904Sborman /* 65338904Sborman * Call telrcv() once to pick up anything received during 65438904Sborman * terminal type negotiation, 4.2/4.3 determination, and 65538904Sborman * linemode negotiation. 65638904Sborman */ 65738904Sborman telrcv(); 65838904Sborman 65938904Sborman (void) ioctl(f, FIONBIO, (char *)&on); 66038904Sborman (void) ioctl(p, FIONBIO, (char *)&on); 66140242Sborman #if defined(CRAY2) && defined(UNICOS5) 66238904Sborman init_termdriver(f, p, interrupt, sendbrk); 66338904Sborman #endif 66438904Sborman 66538904Sborman #if defined(SO_OOBINLINE) 66638904Sborman (void) setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on); 66738904Sborman #endif /* defined(SO_OOBINLINE) */ 66838904Sborman 66938904Sborman #ifdef SIGTSTP 67038904Sborman (void) signal(SIGTSTP, SIG_IGN); 67138904Sborman #endif 67238904Sborman #ifdef SIGTTOU 67338904Sborman /* 67438904Sborman * Ignoring SIGTTOU keeps the kernel from blocking us 67538904Sborman * in ttioct() in /sys/tty.c. 67638904Sborman */ 67738904Sborman (void) signal(SIGTTOU, SIG_IGN); 67838904Sborman #endif 67938904Sborman 68038904Sborman (void) signal(SIGCHLD, cleanup); 68138904Sborman 68240242Sborman #if defined(CRAY2) && defined(UNICOS5) 68338904Sborman /* 68438904Sborman * Cray-2 will send a signal when pty modes are changed by slave 68538904Sborman * side. Set up signal handler now. 68638904Sborman */ 68738904Sborman if ((int)signal(SIGUSR1, termstat) < 0) 68838904Sborman perror("signal"); 68938904Sborman else if (ioctl(p, TCSIGME, (char *)SIGUSR1) < 0) 69038904Sborman perror("ioctl:TCSIGME"); 69138904Sborman /* 69238904Sborman * Make processing loop check terminal characteristics early on. 69338904Sborman */ 69438904Sborman termstat(); 69538904Sborman #endif 69638904Sborman 697*45234Sborman #ifdef TIOCNOTTY 698*45234Sborman { 699*45234Sborman register int t; 700*45234Sborman t = open(_PATH_TTY, O_RDWR); 701*45234Sborman if (t >= 0) { 702*45234Sborman (void) ioctl(t, TIOCNOTTY, (char *)0); 703*45234Sborman (void) close(t); 704*45234Sborman } 705*45234Sborman } 70640242Sborman #endif 707*45234Sborman 70844363Sborman #if defined(TIOCSCTTY) && defined(CRAY) 709*45234Sborman (void) setsid(); 71044363Sborman ioctl(p, TIOCSCTTY, 0); 71144363Sborman #endif 71238904Sborman 71338904Sborman /* 71412713Ssam * Show banner that getty never gave. 71527797Sminshall * 71633271Sminshall * We put the banner in the pty input buffer. This way, it 71733271Sminshall * gets carriage return null processing, etc., just like all 71833271Sminshall * other pty --> client data. 71912713Ssam */ 72027797Sminshall 721*45234Sborman #if !defined(CRAY) || !defined(NEWINIT) 722*45234Sborman if (getenv("USER")) 723*45234Sborman hostinfo = 0; 724*45234Sborman #endif 72538904Sborman (void) gethostname(hostname, sizeof (hostname)); 72638904Sborman 72733271Sminshall if (getent(defent, "default") == 1) { 72833271Sminshall char *getstr(); 72938904Sborman char *cp=defstrs; 73027649Sminshall 73138904Sborman HE = getstr("he", &cp); 73238904Sborman HN = getstr("hn", &cp); 73338904Sborman IM = getstr("im", &cp); 73433271Sminshall if (HN && *HN) 73538904Sborman (void) strcpy(hostname, HN); 73638904Sborman if (IM == 0) 73738904Sborman IM = ""; 73833271Sminshall } else { 739*45234Sborman IM = DEFAULT_IM; 74038904Sborman HE = 0; 74133271Sminshall } 74238904Sborman edithost(HE, hostname); 743*45234Sborman if (hostinfo && *IM) 74438904Sborman putf(IM, ptyibuf2); 74527797Sminshall 74638904Sborman if (pcc) 74738904Sborman (void) strncat(ptyibuf2, ptyip, pcc+1); 74838904Sborman ptyip = ptyibuf2; 74938904Sborman pcc = strlen(ptyip); 75040242Sborman #ifdef LINEMODE 75140242Sborman /* 75240242Sborman * Last check to make sure all our states are correct. 75340242Sborman */ 75440242Sborman init_termbuf(); 75540242Sborman localstat(); 75640242Sborman #endif /* LINEMODE */ 75733271Sminshall 75844363Sborman #ifdef DIAGNOSTICS 75944363Sborman if (diagnostic & TD_REPORT) { 76044363Sborman sprintf(nfrontp, "td: Entering processing loop\r\n"); 76144363Sborman nfrontp += strlen(nfrontp); 76244363Sborman } 76344363Sborman #endif /* DIAGNOSTICS */ 76444363Sborman 765*45234Sborman #ifdef convex 766*45234Sborman startslave(host); 767*45234Sborman #endif 768*45234Sborman 7696002Sroot for (;;) { 77027185Sminshall fd_set ibits, obits, xbits; 7716002Sroot register int c; 7726002Sroot 77327185Sminshall if (ncc < 0 && pcc < 0) 77427185Sminshall break; 77527185Sminshall 77640242Sborman #if defined(CRAY2) && defined(UNICOS5) 77738904Sborman if (needtermstat) 77838904Sborman _termstat(); 77940242Sborman #endif /* defined(CRAY2) && defined(UNICOS5) */ 78027185Sminshall FD_ZERO(&ibits); 78127185Sminshall FD_ZERO(&obits); 78227185Sminshall FD_ZERO(&xbits); 7836002Sroot /* 7846002Sroot * Never look for input if there's still 7856002Sroot * stuff in the corresponding output buffer 7866002Sroot */ 78727185Sminshall if (nfrontp - nbackp || pcc > 0) { 78827185Sminshall FD_SET(f, &obits); 78927185Sminshall } else { 79027185Sminshall FD_SET(p, &ibits); 79127185Sminshall } 79227185Sminshall if (pfrontp - pbackp || ncc > 0) { 79327185Sminshall FD_SET(p, &obits); 79427185Sminshall } else { 79527185Sminshall FD_SET(f, &ibits); 79627185Sminshall } 79727185Sminshall if (!SYNCHing) { 79827185Sminshall FD_SET(f, &xbits); 79927185Sminshall } 80027185Sminshall if ((c = select(16, &ibits, &obits, &xbits, 80127185Sminshall (struct timeval *)0)) < 1) { 80227185Sminshall if (c == -1) { 80327185Sminshall if (errno == EINTR) { 80427185Sminshall continue; 80527185Sminshall } 80627185Sminshall } 8076002Sroot sleep(5); 8086002Sroot continue; 8096002Sroot } 8106002Sroot 8116002Sroot /* 81227185Sminshall * Any urgent data? 81327185Sminshall */ 81427185Sminshall if (FD_ISSET(net, &xbits)) { 81527185Sminshall SYNCHing = 1; 81627185Sminshall } 81727185Sminshall 81827185Sminshall /* 8196002Sroot * Something to read from the network... 8206002Sroot */ 82127185Sminshall if (FD_ISSET(net, &ibits)) { 82227649Sminshall #if !defined(SO_OOBINLINE) 82327185Sminshall /* 82427898Skarels * In 4.2 (and 4.3 beta) systems, the 82527185Sminshall * OOB indication and data handling in the kernel 82627185Sminshall * is such that if two separate TCP Urgent requests 82727185Sminshall * come in, one byte of TCP data will be overlaid. 82827185Sminshall * This is fatal for Telnet, but we try to live 82927185Sminshall * with it. 83027185Sminshall * 83127185Sminshall * In addition, in 4.2 (and...), a special protocol 83227185Sminshall * is needed to pick up the TCP Urgent data in 83327185Sminshall * the correct sequence. 83427185Sminshall * 83527185Sminshall * What we do is: if we think we are in urgent 83627185Sminshall * mode, we look to see if we are "at the mark". 83727185Sminshall * If we are, we do an OOB receive. If we run 83827185Sminshall * this twice, we will do the OOB receive twice, 83927185Sminshall * but the second will fail, since the second 84027185Sminshall * time we were "at the mark", but there wasn't 84127185Sminshall * any data there (the kernel doesn't reset 84227185Sminshall * "at the mark" until we do a normal read). 84327185Sminshall * Once we've read the OOB data, we go ahead 84427185Sminshall * and do normal reads. 84527185Sminshall * 84627185Sminshall * There is also another problem, which is that 84727185Sminshall * since the OOB byte we read doesn't put us 84827185Sminshall * out of OOB state, and since that byte is most 84927185Sminshall * likely the TELNET DM (data mark), we would 85027185Sminshall * stay in the TELNET SYNCH (SYNCHing) state. 85127185Sminshall * So, clocks to the rescue. If we've "just" 85227185Sminshall * received a DM, then we test for the 85327185Sminshall * presence of OOB data when the receive OOB 85427185Sminshall * fails (and AFTER we did the normal mode read 85527185Sminshall * to clear "at the mark"). 85627185Sminshall */ 85727185Sminshall if (SYNCHing) { 85827185Sminshall int atmark; 85927185Sminshall 86038904Sborman (void) ioctl(net, SIOCATMARK, (char *)&atmark); 86127185Sminshall if (atmark) { 86227185Sminshall ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB); 86327185Sminshall if ((ncc == -1) && (errno == EINVAL)) { 86427185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 86527983Sminshall if (sequenceIs(didnetreceive, gotDM)) { 86627185Sminshall SYNCHing = stilloob(net); 86727185Sminshall } 86827185Sminshall } 86927185Sminshall } else { 87027185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 8716002Sroot } 87227185Sminshall } else { 87327185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 87427185Sminshall } 87527185Sminshall settimer(didnetreceive); 87627649Sminshall #else /* !defined(SO_OOBINLINE)) */ 87727185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 87827649Sminshall #endif /* !defined(SO_OOBINLINE)) */ 87927185Sminshall if (ncc < 0 && errno == EWOULDBLOCK) 88027185Sminshall ncc = 0; 88127185Sminshall else { 88227185Sminshall if (ncc <= 0) { 88327185Sminshall break; 88427185Sminshall } 88527185Sminshall netip = netibuf; 88627185Sminshall } 88744363Sborman #ifdef DIAGNOSTICS 88844363Sborman if (diagnostic & (TD_REPORT | TD_NETDATA)) { 88944363Sborman sprintf(nfrontp, "td: netread %d chars\r\n", ncc); 89044363Sborman nfrontp += strlen(nfrontp); 89144363Sborman } 89244363Sborman if (diagnostic & TD_NETDATA) { 89344363Sborman printdata("nd", netip, ncc); 89444363Sborman } 89544363Sborman #endif /* DIAGNOSTICS */ 8966002Sroot } 8976002Sroot 8986002Sroot /* 8996002Sroot * Something to read from the pty... 9006002Sroot */ 90138904Sborman if (FD_ISSET(p, &ibits)) { 9026002Sroot pcc = read(p, ptyibuf, BUFSIZ); 9036002Sroot if (pcc < 0 && errno == EWOULDBLOCK) 9046002Sroot pcc = 0; 9056002Sroot else { 9066002Sroot if (pcc <= 0) 9076002Sroot break; 90840242Sborman #if !defined(CRAY2) || !defined(UNICOS5) 90938904Sborman #ifdef LINEMODE 91038904Sborman /* 91138904Sborman * If ioctl from pty, pass it through net 91238904Sborman */ 91338904Sborman if (ptyibuf[0] & TIOCPKT_IOCTL) { 91438904Sborman copy_termbuf(ptyibuf+1, pcc-1); 91538904Sborman localstat(); 91638904Sborman pcc = 1; 91738904Sborman } 91838904Sborman #endif LINEMODE 91937210Sminshall if (ptyibuf[0] & TIOCPKT_FLUSHWRITE) { 92038904Sborman netclear(); /* clear buffer back */ 921*45234Sborman #ifndef NO_URGENT 92240242Sborman /* 923*45234Sborman * There are client telnets on some 92440242Sborman * operating systems get screwed up 92540242Sborman * royally if we send them urgent 926*45234Sborman * mode data. 92740242Sborman */ 92837210Sminshall *nfrontp++ = IAC; 92937210Sminshall *nfrontp++ = DM; 93037210Sminshall neturg = nfrontp-1; /* off by one XXX */ 93140242Sborman #endif 93237210Sminshall } 93344363Sborman if (his_state_is_will(TELOPT_LFLOW) && 93437210Sminshall (ptyibuf[0] & 93538904Sborman (TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))) { 93638904Sborman (void) sprintf(nfrontp, "%c%c%c%c%c%c", 93737210Sminshall IAC, SB, TELOPT_LFLOW, 93837210Sminshall ptyibuf[0] & TIOCPKT_DOSTOP ? 1 : 0, 93937210Sminshall IAC, SE); 94037210Sminshall nfrontp += 6; 94137210Sminshall } 94233267Sminshall pcc--; 94333267Sminshall ptyip = ptyibuf+1; 94440242Sborman #else /* defined(CRAY2) && defined(UNICOS5) */ 94538904Sborman if (!uselinemode) { 94639531Sborman unpcc = pcc; 94739531Sborman unptyip = ptyibuf; 94839531Sborman pcc = term_output(&unptyip, ptyibuf2, 94939531Sborman &unpcc, BUFSIZ); 95038904Sborman ptyip = ptyibuf2; 95138904Sborman } else 95238904Sborman ptyip = ptyibuf; 95340242Sborman #endif /* defined(CRAY2) && defined(UNICOS5) */ 95438904Sborman } 9556002Sroot } 9566002Sroot 9576002Sroot while (pcc > 0) { 9586002Sroot if ((&netobuf[BUFSIZ] - nfrontp) < 2) 9596002Sroot break; 9606002Sroot c = *ptyip++ & 0377, pcc--; 9616002Sroot if (c == IAC) 9626002Sroot *nfrontp++ = c; 96340242Sborman #if defined(CRAY2) && defined(UNICOS5) 96438904Sborman else if (c == '\n' && 96544363Sborman my_state_is_wont(TELOPT_BINARY) && newmap) 96638904Sborman *nfrontp++ = '\r'; 96740242Sborman #endif /* defined(CRAY2) && defined(UNICOS5) */ 9686002Sroot *nfrontp++ = c; 96944363Sborman if ((c == '\r') && (my_state_is_wont(TELOPT_BINARY))) { 97027020Sminshall if (pcc > 0 && ((*ptyip & 0377) == '\n')) { 97127020Sminshall *nfrontp++ = *ptyip++ & 0377; 97227020Sminshall pcc--; 97327020Sminshall } else 97427020Sminshall *nfrontp++ = '\0'; 97527020Sminshall } 9766002Sroot } 97740242Sborman #if defined(CRAY2) && defined(UNICOS5) 97839531Sborman /* 97939531Sborman * If chars were left over from the terminal driver, 98039531Sborman * note their existence. 98139531Sborman */ 98239531Sborman if (!uselinemode && unpcc) { 98339531Sborman pcc = unpcc; 98439531Sborman unpcc = 0; 98539531Sborman ptyip = unptyip; 98639531Sborman } 98740242Sborman #endif /* defined(CRAY2) && defined(UNICOS5) */ 98839531Sborman 98927185Sminshall if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0) 9906002Sroot netflush(); 9916002Sroot if (ncc > 0) 9926002Sroot telrcv(); 99327185Sminshall if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0) 9946002Sroot ptyflush(); 9956002Sroot } 9966002Sroot cleanup(); 99738904Sborman } /* end of telnet */ 9986002Sroot 99938904Sborman #ifndef TCSIG 100038904Sborman # ifdef TIOCSIG 100138904Sborman # define TCSIG TIOCSIG 100238904Sborman # endif 100338904Sborman #endif 10046002Sroot 100537212Sminshall /* 10066002Sroot * Send interrupt to process on other side of pty. 10076002Sroot * If it is in raw mode, just write NULL; 10086002Sroot * otherwise, write intr char. 10096002Sroot */ 10106002Sroot interrupt() 10116002Sroot { 101238904Sborman ptyflush(); /* half-hearted */ 10136002Sroot 101438904Sborman #ifdef TCSIG 101538904Sborman (void) ioctl(pty, TCSIG, (char *)SIGINT); 101638904Sborman #else /* TCSIG */ 101738904Sborman init_termbuf(); 101840242Sborman *pfrontp++ = slctab[SLC_IP].sptr ? 101940242Sborman (unsigned char)*slctab[SLC_IP].sptr : '\177'; 102038904Sborman #endif /* TCSIG */ 10216002Sroot } 10226002Sroot 102327229Sminshall /* 102427229Sminshall * Send quit to process on other side of pty. 102527229Sminshall * If it is in raw mode, just write NULL; 102627229Sminshall * otherwise, write quit char. 102727229Sminshall */ 102827229Sminshall sendbrk() 102927229Sminshall { 103027229Sminshall ptyflush(); /* half-hearted */ 103138904Sborman #ifdef TCSIG 103238904Sborman (void) ioctl(pty, TCSIG, (char *)SIGQUIT); 103338904Sborman #else /* TCSIG */ 103438904Sborman init_termbuf(); 103540242Sborman *pfrontp++ = slctab[SLC_ABORT].sptr ? 103640242Sborman (unsigned char)*slctab[SLC_ABORT].sptr : '\034'; 103738904Sborman #endif /* TCSIG */ 103827229Sminshall } 103927229Sminshall 104038904Sborman sendsusp() 10416002Sroot { 104238904Sborman #ifdef SIGTSTP 104338904Sborman ptyflush(); /* half-hearted */ 104438904Sborman # ifdef TCSIG 104538904Sborman (void) ioctl(pty, TCSIG, (char *)SIGTSTP); 104638904Sborman # else /* TCSIG */ 104740242Sborman *pfrontp++ = slctab[SLC_SUSP].sptr ? 104840242Sborman (unsigned char)*slctab[SLC_SUSP].sptr : '\032'; 104938904Sborman # endif /* TCSIG */ 105038904Sborman #endif /* SIGTSTP */ 10516002Sroot } 10526002Sroot 1053*45234Sborman /* 1054*45234Sborman * When we get an AYT, if ^T is enabled, use that. Otherwise, 1055*45234Sborman * just send back "[Yes]". 1056*45234Sborman */ 1057*45234Sborman recv_ayt() 1058*45234Sborman { 1059*45234Sborman #if defined(SIGINFO) && defined(TCSIG) 1060*45234Sborman if (slctab[SLC_AYT].sptr && *slctab[SLC_AYT].sptr != _POSIX_VDISABLE) { 1061*45234Sborman (void) ioctl(pty, TCSIG, (char *)SIGINFO); 1062*45234Sborman return; 1063*45234Sborman } 1064*45234Sborman #endif 1065*45234Sborman (void) strcpy(nfrontp, "\r\n[Yes]\r\n"); 1066*45234Sborman nfrontp += 9; 1067*45234Sborman } 1068*45234Sborman 106938904Sborman doeof() 10706002Sroot { 1071*45234Sborman #if defined(LINEMODE) && defined(USE_TERMIO) && (VEOF == VMIN) 107240242Sborman extern char oldeofc; 107340242Sborman #endif 107438904Sborman init_termbuf(); 10756002Sroot 1076*45234Sborman #if defined(LINEMODE) && defined(USE_TERMIO) && (VEOF == VMIN) 107740242Sborman if (!tty_isediting()) { 107840242Sborman *pfrontp++ = oldeofc; 107940242Sborman return; 108040242Sborman } 108140242Sborman #endif 108240242Sborman *pfrontp++ = slctab[SLC_EOF].sptr ? 108340242Sborman (unsigned char)*slctab[SLC_EOF].sptr : '\004'; 10846002Sroot } 1085