121182Sdist /* 238904Sborman * Copyright (c) 1989 Regents of the University of California. 333687Sbostic * All rights reserved. 433687Sbostic * 533687Sbostic * Redistribution and use in source and binary forms are permitted 634778Sbostic * provided that the above copyright notice and this paragraph are 734778Sbostic * duplicated in all such forms and that any documentation, 834778Sbostic * advertising materials, and other materials related to such 934778Sbostic * distribution and use acknowledge that the software was developed 1034778Sbostic * by the University of California, Berkeley. The name of the 1134778Sbostic * University may not be used to endorse or promote products derived 1234778Sbostic * from this software without specific prior written permission. 1334778Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 1434778Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 1534778Sbostic * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1621182Sdist */ 1721182Sdist 186295Sroot #ifndef lint 1921182Sdist char copyright[] = 2038904Sborman "@(#) Copyright (c) 1989 Regents of the University of California.\n\ 2121182Sdist All rights reserved.\n"; 2233687Sbostic #endif /* not lint */ 236295Sroot 2421182Sdist #ifndef lint 25*40242Sborman static char sccsid[] = "@(#)telnetd.c 5.42 (Berkeley) 02/28/90"; 2633687Sbostic #endif /* not lint */ 2721182Sdist 2838904Sborman #include "telnetd.h" 2938904Sborman 306002Sroot /* 3138904Sborman * I/O data buffers, 3238904Sborman * pointers, and counters. 336002Sroot */ 3438904Sborman char ptyibuf[BUFSIZ], *ptyip = ptyibuf; 3538904Sborman char ptyibuf2[BUFSIZ]; 369218Ssam 3738904Sborman #ifdef CRAY 3838904Sborman int hostinfo = 1; /* do we print login banner? */ 3938904Sborman #endif 409218Ssam 4138904Sborman #ifdef CRAY 4238904Sborman extern int newmap; /* nonzero if \n maps to ^M^J */ 43*40242Sborman int lowpty = 0, highpty; /* low, high pty numbers */ 4438904Sborman #endif /* CRAY */ 4512216Ssam 4638904Sborman int debug = 0; 4738904Sborman char *progname; 489218Ssam 49*40242Sborman #if defined(IP_TOS) && defined(NEED_GETTOS) 50*40242Sborman struct tosent { 51*40242Sborman char *t_name; /* name */ 52*40242Sborman char **t_aliases; /* alias list */ 53*40242Sborman char *t_proto; /* protocol */ 54*40242Sborman int t_tos; /* Type Of Service bits */ 55*40242Sborman }; 56*40242Sborman 57*40242Sborman struct tosent * 58*40242Sborman gettosbyname(name, proto) 59*40242Sborman char *name, *proto; 60*40242Sborman { 61*40242Sborman static struct tosent te; 62*40242Sborman static char *aliasp = 0; 63*40242Sborman 64*40242Sborman te.t_name = name; 65*40242Sborman te.t_aliases = &aliasp; 66*40242Sborman te.t_proto = proto; 67*40242Sborman te.t_tos = 020; /* Low Delay bit */ 68*40242Sborman return(&te); 69*40242Sborman } 70*40242Sborman #endif 71*40242Sborman 7238904Sborman main(argc, argv) 7338904Sborman char *argv[]; 7438904Sborman { 7538904Sborman struct sockaddr_in from; 7638904Sborman int on = 1, fromlen; 77*40242Sborman #ifdef IP_TOS 78*40242Sborman struct tosent *tp; 79*40242Sborman #endif /* IP_TOS */ 806002Sroot 8138904Sborman pfrontp = pbackp = ptyobuf; 8238904Sborman netip = netibuf; 8338904Sborman nfrontp = nbackp = netobuf; 846002Sroot 8538904Sborman progname = *argv; 86*40242Sborman 87*40242Sborman #ifdef CRAY 88*40242Sborman /* 89*40242Sborman * Get number of pty's before trying to process options, 90*40242Sborman * which may include changing pty range. 91*40242Sborman */ 92*40242Sborman highpty = getnpty(); 93*40242Sborman #endif /* CRAY */ 94*40242Sborman 9538904Sborman top: 9638904Sborman argc--, argv++; 9727649Sminshall 9838904Sborman if (argc > 0 && strcmp(*argv, "-debug") == 0) { 9938904Sborman debug++; 10038904Sborman goto top; 10138904Sborman } 10227649Sminshall 10338904Sborman #ifdef LINEMODE 10438904Sborman if (argc > 0 && !strcmp(*argv, "-l")) { 10538904Sborman alwayslinemode = 1; 10638904Sborman goto top; 10738904Sborman } 10838904Sborman #endif /* LINEMODE */ 10927649Sminshall 11038904Sborman #ifdef CRAY 11138904Sborman if (argc > 0 && !strcmp(*argv, "-h")) { 11238904Sborman hostinfo = 0; 11338904Sborman goto top; 11438904Sborman } 11527649Sminshall 11638904Sborman if (argc > 0 && !strncmp(*argv, "-r", 2)) { 11738904Sborman char *strchr(); 11838904Sborman char *c; 11927649Sminshall 120*40242Sborman /* 121*40242Sborman * Allow the specification of alterations to the pty search 122*40242Sborman * range. It is legal to specify only one, and not change the 123*40242Sborman * other from its default. 124*40242Sborman */ 12538904Sborman *argv += 2; 12638904Sborman if (**argv == '\0' && argc) 12738904Sborman argv++, argc--; 12838904Sborman c = strchr(*argv, '-'); 12938904Sborman if (c) { 13038904Sborman *c++ = '\0'; 13138904Sborman highpty = atoi(c); 132*40242Sborman } 133*40242Sborman if (**argv != '\0') 134*40242Sborman lowpty = atoi(*argv); 135*40242Sborman if ((lowpty > highpty) || (lowpty < 0) || (highpty > 32767)) { 13638904Sborman usage: 13738904Sborman fprintf(stderr, "Usage: telnetd [-debug] [-h] "); 13838904Sborman # ifdef NEWINIT 13938904Sborman fprintf(stderr, "[-Iinitid] "); 14038904Sborman # endif /* NEWINIT */ 141*40242Sborman fprintf(stderr, "[-l] [-r[lowpty]-[highpty]] [port]\n"); 14238904Sborman exit(1); 14338904Sborman } 14438904Sborman goto top; 14538904Sborman } 14638904Sborman # ifdef NEWINIT 14738904Sborman if (argc > 0 && !strncmp(*argv, "-I", 2)) { 14838904Sborman extern char *gen_id; 14927649Sminshall 15038904Sborman *argv += 2; 15138904Sborman if (**argv == '\0') { 15238904Sborman if (argc < 2) 15338904Sborman goto usage; 15438904Sborman argv++, argc--; 15538904Sborman if (**argv == '\0') 15638904Sborman goto usage; 15738904Sborman } 15838904Sborman gen_id = *argv; 15938904Sborman goto top; 16038904Sborman } 16138904Sborman # endif /* NEWINIT */ 16238904Sborman #endif /* CRAY */ 1636002Sroot 16438904Sborman if (debug) { 16527185Sminshall int s, ns, foo; 16627185Sminshall struct servent *sp; 16727185Sminshall static struct sockaddr_in sin = { AF_INET }; 16827185Sminshall 16938904Sborman if (argc > 0) { 17038904Sborman if (sp = getservbyname(*argv, "tcp")) { 17138904Sborman sin.sin_port = sp->s_port; 17238904Sborman } else { 17338904Sborman sin.sin_port = atoi(*argv); 17438904Sborman if ((int)sin.sin_port <= 0) { 17538904Sborman fprintf(stderr, "telnetd: %s: bad port #\n", *argv); 17638904Sborman exit(1); 17738904Sborman } 17838904Sborman sin.sin_port = htons((u_short)sin.sin_port); 17938904Sborman } 18037210Sminshall } else { 18137210Sminshall sp = getservbyname("telnet", "tcp"); 18237210Sminshall if (sp == 0) { 18337210Sminshall fprintf(stderr, 18437210Sminshall "telnetd: tcp/telnet: unknown service\n"); 18538904Sborman exit(1); 18637210Sminshall } 18737210Sminshall sin.sin_port = sp->s_port; 18827185Sminshall } 18927185Sminshall 19027185Sminshall s = socket(AF_INET, SOCK_STREAM, 0); 19127185Sminshall if (s < 0) { 19227185Sminshall perror("telnetd: socket");; 19327185Sminshall exit(1); 19427185Sminshall } 19538904Sborman (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); 19638904Sborman if (bind(s, (struct sockaddr *)&sin, sizeof sin) < 0) { 19727185Sminshall perror("bind"); 19827185Sminshall exit(1); 19927185Sminshall } 20027185Sminshall if (listen(s, 1) < 0) { 20127185Sminshall perror("listen"); 20227185Sminshall exit(1); 20327185Sminshall } 20427185Sminshall foo = sizeof sin; 20538904Sborman ns = accept(s, (struct sockaddr *)&sin, &foo); 20627185Sminshall if (ns < 0) { 20727185Sminshall perror("accept"); 20827185Sminshall exit(1); 20927185Sminshall } 21038904Sborman (void) dup2(ns, 0); 21138904Sborman (void) close(ns); 21238904Sborman (void) close(s); 21327185Sminshall } 21438904Sborman 21524855Seric openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON); 21616371Skarels fromlen = sizeof (from); 21738904Sborman if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) { 21838904Sborman fprintf(stderr, "%s: ", progname); 21916371Skarels perror("getpeername"); 22016371Skarels _exit(1); 2218346Ssam } 22217156Ssam if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) { 22317187Sralph syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); 22410418Ssam } 225*40242Sborman 226*40242Sborman #ifdef IP_TOS 227*40242Sborman if ((tp = gettosbyname("telnet", "tcp")) && 228*40242Sborman (setsockopt(0, IPPROTO_IP, IP_TOS, &tp->t_tos, sizeof(int)) < 0)) 229*40242Sborman syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); 230*40242Sborman #endif /* IP_TOS */ 23138904Sborman net = 0; 23238904Sborman doit(&from); 23338904Sborman /* NOTREACHED */ 23438904Sborman } /* end of main */ 2356002Sroot 236*40242Sborman void cleanup(); 23727649Sminshall 23827649Sminshall /* 23927983Sminshall * getterminaltype 24027649Sminshall * 24138904Sborman * Ask the other end to send along its terminal type and speed. 24227983Sminshall * Output is the variable terminaltype filled in. 24327649Sminshall */ 24438904Sborman static char ttytype_sbbuf[] = { IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE }; 24527983Sminshall void 24627983Sminshall getterminaltype() 24727649Sminshall { 24838904Sborman void ttloop(); 24927649Sminshall 25038904Sborman settimer(baseline); 25139503Sborman send_do(TELOPT_TTYPE, 1); 25239503Sborman send_do(TELOPT_TSPEED, 1); 25338904Sborman while ((hiswants[TELOPT_TTYPE] != hisopts[TELOPT_TTYPE]) || 25438904Sborman (hiswants[TELOPT_TSPEED] != hisopts[TELOPT_TSPEED])) { 25527983Sminshall ttloop(); 25627649Sminshall } 25738904Sborman if (hisopts[TELOPT_TSPEED] == OPT_YES) { 25838904Sborman static char sbbuf[] = { IAC, SB, TELOPT_TSPEED, TELQUAL_SEND, IAC, SE }; 25927983Sminshall 26027983Sminshall bcopy(sbbuf, nfrontp, sizeof sbbuf); 26127983Sminshall nfrontp += sizeof sbbuf; 26238904Sborman } 26338904Sborman if (hisopts[TELOPT_TTYPE] == OPT_YES) { 26438904Sborman 26538904Sborman bcopy(ttytype_sbbuf, nfrontp, sizeof ttytype_sbbuf); 26638904Sborman nfrontp += sizeof ttytype_sbbuf; 26738904Sborman } 26838904Sborman if (hisopts[TELOPT_TSPEED] == OPT_YES) { 26938904Sborman while (sequenceIs(tspeedsubopt, baseline)) 27027983Sminshall ttloop(); 27138904Sborman } 27238904Sborman if (hisopts[TELOPT_TTYPE] == OPT_YES) { 27338904Sborman char first[256], last[256]; 27438904Sborman 27538904Sborman while (sequenceIs(ttypesubopt, baseline)) 27638904Sborman ttloop(); 27738904Sborman 27838904Sborman if (!terminaltypeok(&terminaltype[5])) { 27938904Sborman (void) strncpy(first, terminaltype, sizeof(first)); 28038904Sborman for(;;) { 28138904Sborman /* 28238904Sborman * Save the unknown name, and request the next name. 28338904Sborman */ 28438904Sborman (void) strncpy(last, terminaltype, sizeof(last)); 28538904Sborman _gettermname(); 28638904Sborman if (terminaltypeok(&terminaltype[5])) 28738904Sborman break; 28838904Sborman if (strncmp(last, terminaltype, sizeof(last)) == 0) { 28938904Sborman /* 29038904Sborman * We've hit the end. If this is the same as 29138904Sborman * the first name, just go with it. 29238904Sborman */ 29338904Sborman if (strncmp(first, terminaltype, sizeof(first) == 0)) 29438904Sborman break; 29538904Sborman /* 29638904Sborman * Get the terminal name one more type, so that 29738904Sborman * RFC1091 compliant telnets will cycle back to 29838904Sborman * the start of the list. 29938904Sborman */ 30038904Sborman _gettermname(); 30138904Sborman if (strncmp(first, terminaltype, sizeof(first) != 0)) 30238904Sborman (void) strncpy(terminaltype, first, sizeof(first)); 30338904Sborman break; 30438904Sborman } 30538904Sborman } 30627983Sminshall } 30727983Sminshall } 30838904Sborman } /* end of getterminaltype */ 30938904Sborman 31038904Sborman _gettermname() 31138904Sborman { 31238904Sborman settimer(baseline); 31338904Sborman bcopy(ttytype_sbbuf, nfrontp, sizeof ttytype_sbbuf); 31438904Sborman nfrontp += sizeof ttytype_sbbuf; 31538904Sborman while (sequenceIs(ttypesubopt, baseline)) 31638904Sborman ttloop(); 31727649Sminshall } 31827649Sminshall 31938904Sborman terminaltypeok(s) 32038904Sborman char *s; 32138904Sborman { 32238904Sborman char buf[1024]; 32338904Sborman 32438904Sborman if (terminaltype == NULL) 32538904Sborman return(1); 32638904Sborman 32738904Sborman /* 32838904Sborman * tgetent() will return 1 if the type is known, and 32938904Sborman * 0 if it is not known. If it returns -1, it couldn't 33038904Sborman * open the database. But if we can't open the database, 33138904Sborman * it won't help to say we failed, because we won't be 33238904Sborman * able to verify anything else. So, we treat -1 like 1. 33338904Sborman */ 33438904Sborman if (tgetent(buf, s) == 0) 33538904Sborman return(0); 33638904Sborman return(1); 33738904Sborman } 33838904Sborman 3396002Sroot /* 3406002Sroot * Get a pty, scan input lines. 3416002Sroot */ 34238904Sborman doit(who) 34312683Ssam struct sockaddr_in *who; 3446002Sroot { 34520188Skarels char *host, *inet_ntoa(); 34638904Sborman int t; 34712683Ssam struct hostent *hp; 3486002Sroot 34938904Sborman /* 35038904Sborman * Find an available pty to use. 35138904Sborman */ 35238904Sborman pty = getpty(); 35338904Sborman if (pty < 0) 35438904Sborman fatal(net, "All network ports in use"); 35520188Skarels 35638904Sborman t = getptyslave(); 35738904Sborman 35838904Sborman /* get name of connected client */ 35938904Sborman hp = gethostbyaddr((char *)&who->sin_addr, sizeof (struct in_addr), 36012683Ssam who->sin_family); 36112683Ssam if (hp) 36212683Ssam host = hp->h_name; 36312683Ssam else 36417444Sralph host = inet_ntoa(who->sin_addr); 36527983Sminshall 36627983Sminshall /* 36738904Sborman * get terminal type. 36827983Sminshall */ 36927983Sminshall getterminaltype(); 37038904Sborman if (terminaltype == NULL) 37138904Sborman terminaltype = "TERM=network"; 37227983Sminshall 37327649Sminshall /* 37438904Sborman * Start up the login process on the slave side of the terminal 37527649Sminshall */ 37638904Sborman startslave(t, host); 37738904Sborman 37838904Sborman telnet(net, pty); /* begin server processing */ 3799244Ssam /*NOTREACHED*/ 38038904Sborman } /* end of doit */ 3819244Ssam 38238904Sborman #ifndef MAXHOSTNAMELEN 38338904Sborman #define MAXHOSTNAMELEN 64 38438904Sborman #endif MAXHOSTNAMELEN 3856002Sroot /* 3866002Sroot * Main loop. Select from pty and network, and 3876002Sroot * hand data to telnet receiver finite state machine. 3886002Sroot */ 3896002Sroot telnet(f, p) 39038904Sborman int f, p; 3916002Sroot { 3926002Sroot int on = 1; 39327898Skarels char hostname[MAXHOSTNAMELEN]; 394*40242Sborman #if defined(CRAY2) && defined(UNICOS5) 39538904Sborman int termstat(); 39638904Sborman int interrupt(), sendbrk(); 39738904Sborman #endif 39833271Sminshall #define TABBUFSIZ 512 39933271Sminshall char defent[TABBUFSIZ]; 40033271Sminshall char defstrs[TABBUFSIZ]; 40133271Sminshall #undef TABBUFSIZ 40233271Sminshall char *HE; 40333271Sminshall char *HN; 40433271Sminshall char *IM; 40538904Sborman void netflush(); 40638904Sborman 40732400Sminshall /* 40838904Sborman * Initialize the slc mapping table. 40932400Sminshall */ 41038904Sborman get_slc_defaults(); 4116002Sroot 4128379Ssam /* 41338904Sborman * Do some tests where it is desireable to wait for a response. 41438904Sborman * Rather than doing them slowly, one at a time, do them all 41538904Sborman * at once. 4168379Ssam */ 41738904Sborman if (!myopts[TELOPT_SGA]) 41839503Sborman send_will(TELOPT_SGA, 1); 41912713Ssam /* 42027649Sminshall * Is the client side a 4.2 (NOT 4.3) system? We need to know this 42127649Sminshall * because 4.2 clients are unable to deal with TCP urgent data. 42227649Sminshall * 42327649Sminshall * To find out, we send out a "DO ECHO". If the remote system 42427649Sminshall * answers "WILL ECHO" it is probably a 4.2 client, and we note 42527649Sminshall * that fact ("WILL ECHO" ==> that the client will echo what 42627649Sminshall * WE, the server, sends it; it does NOT mean that the client will 42727649Sminshall * echo the terminal input). 42827649Sminshall */ 42939503Sborman send_do(TELOPT_ECHO, 1); 43027649Sminshall 43138904Sborman #ifdef LINEMODE 43238904Sborman if (hisopts[TELOPT_LINEMODE] == OPT_NO) { 43338904Sborman /* Query the peer for linemode support by trying to negotiate 43438904Sborman * the linemode option. 43538904Sborman */ 43638904Sborman linemode = 1; 43738904Sborman editmode = 0; 43839503Sborman send_do(TELOPT_LINEMODE, 1); /* send do linemode */ 43938904Sborman } 44038904Sborman #endif /* LINEMODE */ 44138904Sborman 44227649Sminshall /* 44338904Sborman * Send along a couple of other options that we wish to negotiate. 44438904Sborman */ 44539503Sborman send_do(TELOPT_NAWS, 1); 44639503Sborman send_will(TELOPT_STATUS, 1); 44738904Sborman flowmode = 1; /* default flow control state */ 44839503Sborman send_do(TELOPT_LFLOW, 1); 44938904Sborman 45038904Sborman /* 45138904Sborman * Spin, waiting for a response from the DO ECHO. However, 45238904Sborman * some REALLY DUMB telnets out there might not respond 45338904Sborman * to the DO ECHO. So, we spin looking for NAWS, (most dumb 45438904Sborman * telnets so far seem to respond with WONT for a DO that 45538904Sborman * they don't understand...) because by the time we get the 45638904Sborman * response, it will already have processed the DO ECHO. 45738904Sborman * Kludge upon kludge. 45838904Sborman */ 45938904Sborman while (hiswants[TELOPT_NAWS] != hisopts[TELOPT_NAWS]) 46038904Sborman ttloop(); 46138904Sborman 46238904Sborman /* 46338995Sborman * On the off chance that the telnet client is broken and does not 46438995Sborman * respond to the DO ECHO we sent, (after all, we did send the 46538995Sborman * DO NAWS negotiation after the DO ECHO, and we won't get here 46638995Sborman * until a response to the DO NAWS comes back) simulate the 46738995Sborman * receipt of a will echo. This will also send a WONT ECHO 46838995Sborman * to the client, since we assume that the client failed to 46938995Sborman * respond because it believes that it is already in DO ECHO 47038995Sborman * mode, which we do not want. 47138995Sborman */ 472*40242Sborman if (hiswants[TELOPT_ECHO] == OPT_YES) { 47339503Sborman willoption(TELOPT_ECHO); 474*40242Sborman } 47538995Sborman 47638995Sborman /* 47738995Sborman * Finally, to clean things up, we turn on our echo. This 47838995Sborman * will break stupid 4.2 telnets out of local terminal echo. 47938995Sborman */ 48038995Sborman 48138995Sborman if (!myopts[TELOPT_ECHO]) 48239503Sborman send_will(TELOPT_ECHO, 1); 48338995Sborman 48438995Sborman /* 48538904Sborman * Turn on packet mode, and default to line at at time mode. 48638904Sborman */ 48738904Sborman (void) ioctl(p, TIOCPKT, (char *)&on); 48838904Sborman #ifdef LINEMODE 48938904Sborman tty_setlinemode(1); 49038904Sborman 49138904Sborman # ifdef KLUDGELINEMODE 49238904Sborman /* 49338904Sborman * Continuing line mode support. If client does not support 49438904Sborman * real linemode, attempt to negotiate kludge linemode by sending 49538904Sborman * the do timing mark sequence. 49638904Sborman */ 49738904Sborman if (lmodetype < REAL_LINEMODE) 49839503Sborman send_do(TELOPT_TM, 1); 49938904Sborman # endif /* KLUDGELINEMODE */ 50038904Sborman #endif /* LINEMODE */ 50138904Sborman 50238904Sborman /* 50338904Sborman * Call telrcv() once to pick up anything received during 50438904Sborman * terminal type negotiation, 4.2/4.3 determination, and 50538904Sborman * linemode negotiation. 50638904Sborman */ 50738904Sborman telrcv(); 50838904Sborman 50938904Sborman (void) ioctl(f, FIONBIO, (char *)&on); 51038904Sborman (void) ioctl(p, FIONBIO, (char *)&on); 511*40242Sborman #if defined(CRAY2) && defined(UNICOS5) 51238904Sborman init_termdriver(f, p, interrupt, sendbrk); 51338904Sborman #endif 51438904Sborman 51538904Sborman #if defined(SO_OOBINLINE) 51638904Sborman (void) setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on); 51738904Sborman #endif /* defined(SO_OOBINLINE) */ 51838904Sborman 51938904Sborman #ifdef SIGTSTP 52038904Sborman (void) signal(SIGTSTP, SIG_IGN); 52138904Sborman #endif 52238904Sborman #ifdef SIGTTOU 52338904Sborman /* 52438904Sborman * Ignoring SIGTTOU keeps the kernel from blocking us 52538904Sborman * in ttioct() in /sys/tty.c. 52638904Sborman */ 52738904Sborman (void) signal(SIGTTOU, SIG_IGN); 52838904Sborman #endif 52938904Sborman 53038904Sborman (void) signal(SIGCHLD, cleanup); 53138904Sborman 532*40242Sborman #if defined(CRAY2) && defined(UNICOS5) 53338904Sborman /* 53438904Sborman * Cray-2 will send a signal when pty modes are changed by slave 53538904Sborman * side. Set up signal handler now. 53638904Sborman */ 53738904Sborman if ((int)signal(SIGUSR1, termstat) < 0) 53838904Sborman perror("signal"); 53938904Sborman else if (ioctl(p, TCSIGME, (char *)SIGUSR1) < 0) 54038904Sborman perror("ioctl:TCSIGME"); 54138904Sborman /* 54238904Sborman * Make processing loop check terminal characteristics early on. 54338904Sborman */ 54438904Sborman termstat(); 54538904Sborman #endif 54638904Sborman 54738904Sborman (void) setpgrp(0, 0); 548*40242Sborman #ifdef TCSETCTTY 549*40242Sborman ioctl(p, TCSETCTTY, 0); 550*40242Sborman #endif 55138904Sborman 55238904Sborman /* 55312713Ssam * Show banner that getty never gave. 55427797Sminshall * 55533271Sminshall * We put the banner in the pty input buffer. This way, it 55633271Sminshall * gets carriage return null processing, etc., just like all 55733271Sminshall * other pty --> client data. 55812713Ssam */ 55927797Sminshall 56038904Sborman (void) gethostname(hostname, sizeof (hostname)); 56138904Sborman 56233271Sminshall if (getent(defent, "default") == 1) { 56333271Sminshall char *getstr(); 56438904Sborman char *cp=defstrs; 56527649Sminshall 56638904Sborman HE = getstr("he", &cp); 56738904Sborman HN = getstr("hn", &cp); 56838904Sborman IM = getstr("im", &cp); 56933271Sminshall if (HN && *HN) 57038904Sborman (void) strcpy(hostname, HN); 57138904Sborman if (IM == 0) 57238904Sborman IM = ""; 57333271Sminshall } else { 57438904Sborman #ifdef CRAY 57538904Sborman if (hostinfo == 0) 57638904Sborman IM = 0; 57738904Sborman else 57838904Sborman #endif 57938904Sborman IM = DEFAULT_IM; 58038904Sborman HE = 0; 58133271Sminshall } 58238904Sborman edithost(HE, hostname); 58338904Sborman if (IM && *IM) 58438904Sborman putf(IM, ptyibuf2); 58527797Sminshall 58638904Sborman if (pcc) 58738904Sborman (void) strncat(ptyibuf2, ptyip, pcc+1); 58838904Sborman ptyip = ptyibuf2; 58938904Sborman pcc = strlen(ptyip); 590*40242Sborman #ifdef LINEMODE 591*40242Sborman /* 592*40242Sborman * Last check to make sure all our states are correct. 593*40242Sborman */ 594*40242Sborman init_termbuf(); 595*40242Sborman localstat(); 596*40242Sborman #endif /* LINEMODE */ 59733271Sminshall 5986002Sroot for (;;) { 59927185Sminshall fd_set ibits, obits, xbits; 6006002Sroot register int c; 6016002Sroot 60227185Sminshall if (ncc < 0 && pcc < 0) 60327185Sminshall break; 60427185Sminshall 605*40242Sborman #if defined(CRAY2) && defined(UNICOS5) 60638904Sborman if (needtermstat) 60738904Sborman _termstat(); 608*40242Sborman #endif /* defined(CRAY2) && defined(UNICOS5) */ 60927185Sminshall FD_ZERO(&ibits); 61027185Sminshall FD_ZERO(&obits); 61127185Sminshall FD_ZERO(&xbits); 6126002Sroot /* 6136002Sroot * Never look for input if there's still 6146002Sroot * stuff in the corresponding output buffer 6156002Sroot */ 61627185Sminshall if (nfrontp - nbackp || pcc > 0) { 61727185Sminshall FD_SET(f, &obits); 61827185Sminshall } else { 61927185Sminshall FD_SET(p, &ibits); 62027185Sminshall } 62127185Sminshall if (pfrontp - pbackp || ncc > 0) { 62227185Sminshall FD_SET(p, &obits); 62327185Sminshall } else { 62427185Sminshall FD_SET(f, &ibits); 62527185Sminshall } 62627185Sminshall if (!SYNCHing) { 62727185Sminshall FD_SET(f, &xbits); 62827185Sminshall } 62927185Sminshall if ((c = select(16, &ibits, &obits, &xbits, 63027185Sminshall (struct timeval *)0)) < 1) { 63127185Sminshall if (c == -1) { 63227185Sminshall if (errno == EINTR) { 63327185Sminshall continue; 63427185Sminshall } 63527185Sminshall } 6366002Sroot sleep(5); 6376002Sroot continue; 6386002Sroot } 6396002Sroot 6406002Sroot /* 64127185Sminshall * Any urgent data? 64227185Sminshall */ 64327185Sminshall if (FD_ISSET(net, &xbits)) { 64427185Sminshall SYNCHing = 1; 64527185Sminshall } 64627185Sminshall 64727185Sminshall /* 6486002Sroot * Something to read from the network... 6496002Sroot */ 65027185Sminshall if (FD_ISSET(net, &ibits)) { 65127649Sminshall #if !defined(SO_OOBINLINE) 65227185Sminshall /* 65327898Skarels * In 4.2 (and 4.3 beta) systems, the 65427185Sminshall * OOB indication and data handling in the kernel 65527185Sminshall * is such that if two separate TCP Urgent requests 65627185Sminshall * come in, one byte of TCP data will be overlaid. 65727185Sminshall * This is fatal for Telnet, but we try to live 65827185Sminshall * with it. 65927185Sminshall * 66027185Sminshall * In addition, in 4.2 (and...), a special protocol 66127185Sminshall * is needed to pick up the TCP Urgent data in 66227185Sminshall * the correct sequence. 66327185Sminshall * 66427185Sminshall * What we do is: if we think we are in urgent 66527185Sminshall * mode, we look to see if we are "at the mark". 66627185Sminshall * If we are, we do an OOB receive. If we run 66727185Sminshall * this twice, we will do the OOB receive twice, 66827185Sminshall * but the second will fail, since the second 66927185Sminshall * time we were "at the mark", but there wasn't 67027185Sminshall * any data there (the kernel doesn't reset 67127185Sminshall * "at the mark" until we do a normal read). 67227185Sminshall * Once we've read the OOB data, we go ahead 67327185Sminshall * and do normal reads. 67427185Sminshall * 67527185Sminshall * There is also another problem, which is that 67627185Sminshall * since the OOB byte we read doesn't put us 67727185Sminshall * out of OOB state, and since that byte is most 67827185Sminshall * likely the TELNET DM (data mark), we would 67927185Sminshall * stay in the TELNET SYNCH (SYNCHing) state. 68027185Sminshall * So, clocks to the rescue. If we've "just" 68127185Sminshall * received a DM, then we test for the 68227185Sminshall * presence of OOB data when the receive OOB 68327185Sminshall * fails (and AFTER we did the normal mode read 68427185Sminshall * to clear "at the mark"). 68527185Sminshall */ 68627185Sminshall if (SYNCHing) { 68727185Sminshall int atmark; 68827185Sminshall 68938904Sborman (void) ioctl(net, SIOCATMARK, (char *)&atmark); 69027185Sminshall if (atmark) { 69127185Sminshall ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB); 69227185Sminshall if ((ncc == -1) && (errno == EINVAL)) { 69327185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 69427983Sminshall if (sequenceIs(didnetreceive, gotDM)) { 69527185Sminshall SYNCHing = stilloob(net); 69627185Sminshall } 69727185Sminshall } 69827185Sminshall } else { 69927185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 7006002Sroot } 70127185Sminshall } else { 70227185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 70327185Sminshall } 70427185Sminshall settimer(didnetreceive); 70527649Sminshall #else /* !defined(SO_OOBINLINE)) */ 70627185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 70727649Sminshall #endif /* !defined(SO_OOBINLINE)) */ 70827185Sminshall if (ncc < 0 && errno == EWOULDBLOCK) 70927185Sminshall ncc = 0; 71027185Sminshall else { 71127185Sminshall if (ncc <= 0) { 71227185Sminshall break; 71327185Sminshall } 71427185Sminshall netip = netibuf; 71527185Sminshall } 7166002Sroot } 7176002Sroot 7186002Sroot /* 7196002Sroot * Something to read from the pty... 7206002Sroot */ 72138904Sborman if (FD_ISSET(p, &ibits)) { 7226002Sroot pcc = read(p, ptyibuf, BUFSIZ); 7236002Sroot if (pcc < 0 && errno == EWOULDBLOCK) 7246002Sroot pcc = 0; 7256002Sroot else { 7266002Sroot if (pcc <= 0) 7276002Sroot break; 728*40242Sborman #if !defined(CRAY2) || !defined(UNICOS5) 72938904Sborman #ifdef LINEMODE 73038904Sborman /* 73138904Sborman * If ioctl from pty, pass it through net 73238904Sborman */ 73338904Sborman if (ptyibuf[0] & TIOCPKT_IOCTL) { 73438904Sborman copy_termbuf(ptyibuf+1, pcc-1); 73538904Sborman localstat(); 73638904Sborman pcc = 1; 73738904Sborman } 73838904Sborman #endif LINEMODE 73937210Sminshall if (ptyibuf[0] & TIOCPKT_FLUSHWRITE) { 74038904Sborman netclear(); /* clear buffer back */ 741*40242Sborman #ifdef notdef 742*40242Sborman /* 743*40242Sborman * We really should have this in, but 744*40242Sborman * there are client telnets on some 745*40242Sborman * operating systems get screwed up 746*40242Sborman * royally if we send them urgent 747*40242Sborman * mode data. So, for now, we'll not 748*40242Sborman * do this... 749*40242Sborman */ 75037210Sminshall *nfrontp++ = IAC; 75137210Sminshall *nfrontp++ = DM; 75237210Sminshall neturg = nfrontp-1; /* off by one XXX */ 753*40242Sborman #endif 75437210Sminshall } 75537210Sminshall if (hisopts[TELOPT_LFLOW] && 75637210Sminshall (ptyibuf[0] & 75738904Sborman (TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))) { 75838904Sborman (void) sprintf(nfrontp, "%c%c%c%c%c%c", 75937210Sminshall IAC, SB, TELOPT_LFLOW, 76037210Sminshall ptyibuf[0] & TIOCPKT_DOSTOP ? 1 : 0, 76137210Sminshall IAC, SE); 76237210Sminshall nfrontp += 6; 76337210Sminshall } 76433267Sminshall pcc--; 76533267Sminshall ptyip = ptyibuf+1; 766*40242Sborman #else /* defined(CRAY2) && defined(UNICOS5) */ 76738904Sborman if (!uselinemode) { 76839531Sborman unpcc = pcc; 76939531Sborman unptyip = ptyibuf; 77039531Sborman pcc = term_output(&unptyip, ptyibuf2, 77139531Sborman &unpcc, BUFSIZ); 77238904Sborman ptyip = ptyibuf2; 77338904Sborman } else 77438904Sborman ptyip = ptyibuf; 775*40242Sborman #endif /* defined(CRAY2) && defined(UNICOS5) */ 77638904Sborman } 7776002Sroot } 7786002Sroot 7796002Sroot while (pcc > 0) { 7806002Sroot if ((&netobuf[BUFSIZ] - nfrontp) < 2) 7816002Sroot break; 7826002Sroot c = *ptyip++ & 0377, pcc--; 7836002Sroot if (c == IAC) 7846002Sroot *nfrontp++ = c; 785*40242Sborman #if defined(CRAY2) && defined(UNICOS5) 78638904Sborman else if (c == '\n' && 78738904Sborman myopts[TELOPT_BINARY] == OPT_NO && newmap) 78838904Sborman *nfrontp++ = '\r'; 789*40242Sborman #endif /* defined(CRAY2) && defined(UNICOS5) */ 7906002Sroot *nfrontp++ = c; 79131940Sbostic if ((c == '\r') && (myopts[TELOPT_BINARY] == OPT_NO)) { 79227020Sminshall if (pcc > 0 && ((*ptyip & 0377) == '\n')) { 79327020Sminshall *nfrontp++ = *ptyip++ & 0377; 79427020Sminshall pcc--; 79527020Sminshall } else 79627020Sminshall *nfrontp++ = '\0'; 79727020Sminshall } 7986002Sroot } 799*40242Sborman #if defined(CRAY2) && defined(UNICOS5) 80039531Sborman /* 80139531Sborman * If chars were left over from the terminal driver, 80239531Sborman * note their existence. 80339531Sborman */ 80439531Sborman if (!uselinemode && unpcc) { 80539531Sborman pcc = unpcc; 80639531Sborman unpcc = 0; 80739531Sborman ptyip = unptyip; 80839531Sborman } 809*40242Sborman #endif /* defined(CRAY2) && defined(UNICOS5) */ 81039531Sborman 81127185Sminshall if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0) 8126002Sroot netflush(); 8136002Sroot if (ncc > 0) 8146002Sroot telrcv(); 81527185Sminshall if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0) 8166002Sroot ptyflush(); 8176002Sroot } 8186002Sroot cleanup(); 81938904Sborman } /* end of telnet */ 8206002Sroot 82138904Sborman #ifndef TCSIG 82238904Sborman # ifdef TIOCSIG 82338904Sborman # define TCSIG TIOCSIG 82438904Sborman # endif 82538904Sborman #endif 8266002Sroot 82737212Sminshall /* 8286002Sroot * Send interrupt to process on other side of pty. 8296002Sroot * If it is in raw mode, just write NULL; 8306002Sroot * otherwise, write intr char. 8316002Sroot */ 8326002Sroot interrupt() 8336002Sroot { 83438904Sborman ptyflush(); /* half-hearted */ 8356002Sroot 83638904Sborman #ifdef TCSIG 83738904Sborman (void) ioctl(pty, TCSIG, (char *)SIGINT); 83838904Sborman #else /* TCSIG */ 83938904Sborman init_termbuf(); 840*40242Sborman *pfrontp++ = slctab[SLC_IP].sptr ? 841*40242Sborman (unsigned char)*slctab[SLC_IP].sptr : '\177'; 84238904Sborman #endif /* TCSIG */ 8436002Sroot } 8446002Sroot 84527229Sminshall /* 84627229Sminshall * Send quit to process on other side of pty. 84727229Sminshall * If it is in raw mode, just write NULL; 84827229Sminshall * otherwise, write quit char. 84927229Sminshall */ 85027229Sminshall sendbrk() 85127229Sminshall { 85227229Sminshall ptyflush(); /* half-hearted */ 85338904Sborman #ifdef TCSIG 85438904Sborman (void) ioctl(pty, TCSIG, (char *)SIGQUIT); 85538904Sborman #else /* TCSIG */ 85638904Sborman init_termbuf(); 857*40242Sborman *pfrontp++ = slctab[SLC_ABORT].sptr ? 858*40242Sborman (unsigned char)*slctab[SLC_ABORT].sptr : '\034'; 85938904Sborman #endif /* TCSIG */ 86027229Sminshall } 86127229Sminshall 86238904Sborman sendsusp() 8636002Sroot { 86438904Sborman #ifdef SIGTSTP 86538904Sborman ptyflush(); /* half-hearted */ 86638904Sborman # ifdef TCSIG 86738904Sborman (void) ioctl(pty, TCSIG, (char *)SIGTSTP); 86838904Sborman # else /* TCSIG */ 869*40242Sborman *pfrontp++ = slctab[SLC_SUSP].sptr ? 870*40242Sborman (unsigned char)*slctab[SLC_SUSP].sptr : '\032'; 87138904Sborman # endif /* TCSIG */ 87238904Sborman #endif /* SIGTSTP */ 8736002Sroot } 8746002Sroot 87538904Sborman doeof() 8766002Sroot { 877*40242Sborman #if defined(USE_TERMIO) && defined(SYSV_TERMIO) 878*40242Sborman extern char oldeofc; 879*40242Sborman #endif 88038904Sborman init_termbuf(); 8816002Sroot 882*40242Sborman #if defined(USE_TERMIO) && defined(SYSV_TERMIO) 883*40242Sborman if (!tty_isediting()) { 884*40242Sborman *pfrontp++ = oldeofc; 885*40242Sborman return; 886*40242Sborman } 887*40242Sborman #endif 888*40242Sborman *pfrontp++ = slctab[SLC_EOF].sptr ? 889*40242Sborman (unsigned char)*slctab[SLC_EOF].sptr : '\004'; 8906002Sroot } 891