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*38995Sborman static char sccsid[] = "@(#)telnetd.c 5.39 (Berkeley) 09/05/89"; 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 */ 4338904Sborman int lowpty = 0, highpty = 128; /* low, high pty numbers */ 4438904Sborman #endif /* CRAY */ 4512216Ssam 4638904Sborman int debug = 0; 4738904Sborman char *progname; 489218Ssam 4938904Sborman main(argc, argv) 5038904Sborman char *argv[]; 5138904Sborman { 5238904Sborman struct sockaddr_in from; 5338904Sborman int on = 1, fromlen; 546002Sroot 5538904Sborman pfrontp = pbackp = ptyobuf; 5638904Sborman netip = netibuf; 5738904Sborman nfrontp = nbackp = netobuf; 586002Sroot 5938904Sborman progname = *argv; 6038904Sborman top: 6138904Sborman argc--, argv++; 6227649Sminshall 6338904Sborman if (argc > 0 && strcmp(*argv, "-debug") == 0) { 6438904Sborman debug++; 6538904Sborman goto top; 6638904Sborman } 6727649Sminshall 6838904Sborman #ifdef LINEMODE 6938904Sborman if (argc > 0 && !strcmp(*argv, "-l")) { 7038904Sborman alwayslinemode = 1; 7138904Sborman goto top; 7238904Sborman } 7338904Sborman #endif /* LINEMODE */ 7427649Sminshall 7538904Sborman #ifdef CRAY 7638904Sborman if (argc > 0 && !strcmp(*argv, "-h")) { 7738904Sborman hostinfo = 0; 7838904Sborman goto top; 7938904Sborman } 8027649Sminshall 8138904Sborman if (argc > 0 && !strncmp(*argv, "-r", 2)) { 8238904Sborman char *strchr(); 8338904Sborman char *c; 8427649Sminshall 8538904Sborman *argv += 2; 8638904Sborman if (**argv == '\0' && argc) 8738904Sborman argv++, argc--; 8838904Sborman c = strchr(*argv, '-'); 8938904Sborman if (c) { 9038904Sborman *c++ = '\0'; 9138904Sborman highpty = atoi(c); 9238904Sborman } else 9338904Sborman highpty = -1; 9438904Sborman lowpty = atoi(*argv); 9538904Sborman if ((lowpty > highpty) || (lowpty < 0) || (highpty > 999)) { 9638904Sborman usage: 9738904Sborman fprintf(stderr, "Usage: telnetd [-debug] [-h] "); 9838904Sborman # ifdef NEWINIT 9938904Sborman fprintf(stderr, "[-Iinitid] "); 10038904Sborman # endif /* NEWINIT */ 10138904Sborman fprintf(stderr, "[-l] [-rlowpty-highpty] [port]\n"); 10238904Sborman exit(1); 10338904Sborman } 10438904Sborman goto top; 10538904Sborman } 10638904Sborman # ifdef NEWINIT 10738904Sborman if (argc > 0 && !strncmp(*argv, "-I", 2)) { 10838904Sborman extern char *gen_id; 10927649Sminshall 11038904Sborman *argv += 2; 11138904Sborman if (**argv == '\0') { 11238904Sborman if (argc < 2) 11338904Sborman goto usage; 11438904Sborman argv++, argc--; 11538904Sborman if (**argv == '\0') 11638904Sborman goto usage; 11738904Sborman } 11838904Sborman gen_id = *argv; 11938904Sborman goto top; 12038904Sborman } 12138904Sborman # endif /* NEWINIT */ 12238904Sborman #endif /* CRAY */ 1236002Sroot 12438904Sborman if (debug) { 12527185Sminshall int s, ns, foo; 12627185Sminshall struct servent *sp; 12727185Sminshall static struct sockaddr_in sin = { AF_INET }; 12827185Sminshall 12938904Sborman if (argc > 0) { 13038904Sborman if (sp = getservbyname(*argv, "tcp")) { 13138904Sborman sin.sin_port = sp->s_port; 13238904Sborman } else { 13338904Sborman sin.sin_port = atoi(*argv); 13438904Sborman if ((int)sin.sin_port <= 0) { 13538904Sborman fprintf(stderr, "telnetd: %s: bad port #\n", *argv); 13638904Sborman exit(1); 13738904Sborman } 13838904Sborman sin.sin_port = htons((u_short)sin.sin_port); 13938904Sborman } 14037210Sminshall } else { 14137210Sminshall sp = getservbyname("telnet", "tcp"); 14237210Sminshall if (sp == 0) { 14337210Sminshall fprintf(stderr, 14437210Sminshall "telnetd: tcp/telnet: unknown service\n"); 14538904Sborman exit(1); 14637210Sminshall } 14737210Sminshall sin.sin_port = sp->s_port; 14827185Sminshall } 14927185Sminshall 15027185Sminshall s = socket(AF_INET, SOCK_STREAM, 0); 15127185Sminshall if (s < 0) { 15227185Sminshall perror("telnetd: socket");; 15327185Sminshall exit(1); 15427185Sminshall } 15538904Sborman (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); 15638904Sborman if (bind(s, (struct sockaddr *)&sin, sizeof sin) < 0) { 15727185Sminshall perror("bind"); 15827185Sminshall exit(1); 15927185Sminshall } 16027185Sminshall if (listen(s, 1) < 0) { 16127185Sminshall perror("listen"); 16227185Sminshall exit(1); 16327185Sminshall } 16427185Sminshall foo = sizeof sin; 16538904Sborman ns = accept(s, (struct sockaddr *)&sin, &foo); 16627185Sminshall if (ns < 0) { 16727185Sminshall perror("accept"); 16827185Sminshall exit(1); 16927185Sminshall } 17038904Sborman (void) dup2(ns, 0); 17138904Sborman (void) close(ns); 17238904Sborman (void) close(s); 17327185Sminshall } 17438904Sborman 17524855Seric openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON); 17616371Skarels fromlen = sizeof (from); 17738904Sborman if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) { 17838904Sborman fprintf(stderr, "%s: ", progname); 17916371Skarels perror("getpeername"); 18016371Skarels _exit(1); 1818346Ssam } 18217156Ssam if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) { 18317187Sralph syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); 18410418Ssam } 18538904Sborman net = 0; 18638904Sborman doit(&from); 18738904Sborman /* NOTREACHED */ 18838904Sborman } /* end of main */ 1896002Sroot 19027983Sminshall int cleanup(); 19127649Sminshall 19227649Sminshall /* 19327983Sminshall * getterminaltype 19427649Sminshall * 19538904Sborman * Ask the other end to send along its terminal type and speed. 19627983Sminshall * Output is the variable terminaltype filled in. 19727649Sminshall */ 19838904Sborman static char ttytype_sbbuf[] = { IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE }; 19927983Sminshall void 20027983Sminshall getterminaltype() 20127649Sminshall { 20238904Sborman void ttloop(); 20327649Sminshall 20438904Sborman settimer(baseline); 20538904Sborman willoption(TELOPT_TTYPE, 1); 20638904Sborman willoption(TELOPT_TSPEED, 1); 20738904Sborman while ((hiswants[TELOPT_TTYPE] != hisopts[TELOPT_TTYPE]) || 20838904Sborman (hiswants[TELOPT_TSPEED] != hisopts[TELOPT_TSPEED])) { 20927983Sminshall ttloop(); 21027649Sminshall } 21138904Sborman if (hisopts[TELOPT_TSPEED] == OPT_YES) { 21238904Sborman static char sbbuf[] = { IAC, SB, TELOPT_TSPEED, TELQUAL_SEND, IAC, SE }; 21327983Sminshall 21427983Sminshall bcopy(sbbuf, nfrontp, sizeof sbbuf); 21527983Sminshall nfrontp += sizeof sbbuf; 21638904Sborman } 21738904Sborman if (hisopts[TELOPT_TTYPE] == OPT_YES) { 21838904Sborman 21938904Sborman bcopy(ttytype_sbbuf, nfrontp, sizeof ttytype_sbbuf); 22038904Sborman nfrontp += sizeof ttytype_sbbuf; 22138904Sborman } 22238904Sborman if (hisopts[TELOPT_TSPEED] == OPT_YES) { 22338904Sborman while (sequenceIs(tspeedsubopt, baseline)) 22427983Sminshall ttloop(); 22538904Sborman } 22638904Sborman if (hisopts[TELOPT_TTYPE] == OPT_YES) { 22738904Sborman char first[256], last[256]; 22838904Sborman 22938904Sborman while (sequenceIs(ttypesubopt, baseline)) 23038904Sborman ttloop(); 23138904Sborman 23238904Sborman if (!terminaltypeok(&terminaltype[5])) { 23338904Sborman (void) strncpy(first, terminaltype, sizeof(first)); 23438904Sborman for(;;) { 23538904Sborman /* 23638904Sborman * Save the unknown name, and request the next name. 23738904Sborman */ 23838904Sborman (void) strncpy(last, terminaltype, sizeof(last)); 23938904Sborman _gettermname(); 24038904Sborman if (terminaltypeok(&terminaltype[5])) 24138904Sborman break; 24238904Sborman if (strncmp(last, terminaltype, sizeof(last)) == 0) { 24338904Sborman /* 24438904Sborman * We've hit the end. If this is the same as 24538904Sborman * the first name, just go with it. 24638904Sborman */ 24738904Sborman if (strncmp(first, terminaltype, sizeof(first) == 0)) 24838904Sborman break; 24938904Sborman /* 25038904Sborman * Get the terminal name one more type, so that 25138904Sborman * RFC1091 compliant telnets will cycle back to 25238904Sborman * the start of the list. 25338904Sborman */ 25438904Sborman _gettermname(); 25538904Sborman if (strncmp(first, terminaltype, sizeof(first) != 0)) 25638904Sborman (void) strncpy(terminaltype, first, sizeof(first)); 25738904Sborman break; 25838904Sborman } 25938904Sborman } 26027983Sminshall } 26127983Sminshall } 26238904Sborman } /* end of getterminaltype */ 26338904Sborman 26438904Sborman _gettermname() 26538904Sborman { 26638904Sborman settimer(baseline); 26738904Sborman bcopy(ttytype_sbbuf, nfrontp, sizeof ttytype_sbbuf); 26838904Sborman nfrontp += sizeof ttytype_sbbuf; 26938904Sborman while (sequenceIs(ttypesubopt, baseline)) 27038904Sborman ttloop(); 27127649Sminshall } 27227649Sminshall 27338904Sborman terminaltypeok(s) 27438904Sborman char *s; 27538904Sborman { 27638904Sborman char buf[1024]; 27738904Sborman 27838904Sborman if (terminaltype == NULL) 27938904Sborman return(1); 28038904Sborman 28138904Sborman /* 28238904Sborman * tgetent() will return 1 if the type is known, and 28338904Sborman * 0 if it is not known. If it returns -1, it couldn't 28438904Sborman * open the database. But if we can't open the database, 28538904Sborman * it won't help to say we failed, because we won't be 28638904Sborman * able to verify anything else. So, we treat -1 like 1. 28738904Sborman */ 28838904Sborman if (tgetent(buf, s) == 0) 28938904Sborman return(0); 29038904Sborman return(1); 29138904Sborman } 29238904Sborman 2936002Sroot /* 2946002Sroot * Get a pty, scan input lines. 2956002Sroot */ 29638904Sborman doit(who) 29712683Ssam struct sockaddr_in *who; 2986002Sroot { 29920188Skarels char *host, *inet_ntoa(); 30038904Sborman int t; 30112683Ssam struct hostent *hp; 3026002Sroot 30338904Sborman /* 30438904Sborman * Find an available pty to use. 30538904Sborman */ 30638904Sborman pty = getpty(); 30738904Sborman if (pty < 0) 30838904Sborman fatal(net, "All network ports in use"); 30920188Skarels 31038904Sborman t = getptyslave(); 31138904Sborman 31238904Sborman /* get name of connected client */ 31338904Sborman hp = gethostbyaddr((char *)&who->sin_addr, sizeof (struct in_addr), 31412683Ssam who->sin_family); 31512683Ssam if (hp) 31612683Ssam host = hp->h_name; 31712683Ssam else 31817444Sralph host = inet_ntoa(who->sin_addr); 31927983Sminshall 32027983Sminshall /* 32138904Sborman * get terminal type. 32227983Sminshall */ 32327983Sminshall getterminaltype(); 32438904Sborman if (terminaltype == NULL) 32538904Sborman terminaltype = "TERM=network"; 32627983Sminshall 32727649Sminshall /* 32838904Sborman * Start up the login process on the slave side of the terminal 32927649Sminshall */ 33038904Sborman startslave(t, host); 33138904Sborman 33238904Sborman telnet(net, pty); /* begin server processing */ 3339244Ssam /*NOTREACHED*/ 33438904Sborman } /* end of doit */ 3359244Ssam 33638904Sborman #ifndef MAXHOSTNAMELEN 33738904Sborman #define MAXHOSTNAMELEN 64 33838904Sborman #endif MAXHOSTNAMELEN 3396002Sroot /* 3406002Sroot * Main loop. Select from pty and network, and 3416002Sroot * hand data to telnet receiver finite state machine. 3426002Sroot */ 3436002Sroot telnet(f, p) 34438904Sborman int f, p; 3456002Sroot { 3466002Sroot int on = 1; 34727898Skarels char hostname[MAXHOSTNAMELEN]; 34838904Sborman #ifdef CRAY2 34938904Sborman int termstat(); 35038904Sborman int interrupt(), sendbrk(); 35138904Sborman #endif 35233271Sminshall #define TABBUFSIZ 512 35333271Sminshall char defent[TABBUFSIZ]; 35433271Sminshall char defstrs[TABBUFSIZ]; 35533271Sminshall #undef TABBUFSIZ 35633271Sminshall char *HE; 35733271Sminshall char *HN; 35833271Sminshall char *IM; 35938904Sborman void netflush(); 36038904Sborman 36132400Sminshall /* 36238904Sborman * Initialize the slc mapping table. 36332400Sminshall */ 36438904Sborman get_slc_defaults(); 3656002Sroot 3668379Ssam /* 36738904Sborman * Do some tests where it is desireable to wait for a response. 36838904Sborman * Rather than doing them slowly, one at a time, do them all 36938904Sborman * at once. 3708379Ssam */ 37138904Sborman if (!myopts[TELOPT_SGA]) 37238904Sborman dooption(TELOPT_SGA); 37312713Ssam /* 37427649Sminshall * Is the client side a 4.2 (NOT 4.3) system? We need to know this 37527649Sminshall * because 4.2 clients are unable to deal with TCP urgent data. 37627649Sminshall * 37727649Sminshall * To find out, we send out a "DO ECHO". If the remote system 37827649Sminshall * answers "WILL ECHO" it is probably a 4.2 client, and we note 37927649Sminshall * that fact ("WILL ECHO" ==> that the client will echo what 38027649Sminshall * WE, the server, sends it; it does NOT mean that the client will 38127649Sminshall * echo the terminal input). 38227649Sminshall */ 38338904Sborman willoption(TELOPT_ECHO, 1); 38427649Sminshall 38538904Sborman #ifdef LINEMODE 38638904Sborman if (hisopts[TELOPT_LINEMODE] == OPT_NO) { 38738904Sborman /* Query the peer for linemode support by trying to negotiate 38838904Sborman * the linemode option. 38938904Sborman */ 39038904Sborman linemode = 1; 39138904Sborman editmode = 0; 39238904Sborman willoption(TELOPT_LINEMODE, 1); /* send do linemode */ 39338904Sborman } 39438904Sborman #endif /* LINEMODE */ 39538904Sborman 39627649Sminshall /* 39738904Sborman * Send along a couple of other options that we wish to negotiate. 39838904Sborman */ 39938904Sborman willoption(TELOPT_NAWS, 1); 40038904Sborman dooption(TELOPT_STATUS, 1); 40138904Sborman flowmode = 1; /* default flow control state */ 40238904Sborman willoption(TELOPT_LFLOW, 1); 40338904Sborman 40438904Sborman /* 40538904Sborman * Spin, waiting for a response from the DO ECHO. However, 40638904Sborman * some REALLY DUMB telnets out there might not respond 40738904Sborman * to the DO ECHO. So, we spin looking for NAWS, (most dumb 40838904Sborman * telnets so far seem to respond with WONT for a DO that 40938904Sborman * they don't understand...) because by the time we get the 41038904Sborman * response, it will already have processed the DO ECHO. 41138904Sborman * Kludge upon kludge. 41238904Sborman */ 41338904Sborman while (hiswants[TELOPT_NAWS] != hisopts[TELOPT_NAWS]) 41438904Sborman ttloop(); 41538904Sborman 41638904Sborman /* 417*38995Sborman * On the off chance that the telnet client is broken and does not 418*38995Sborman * respond to the DO ECHO we sent, (after all, we did send the 419*38995Sborman * DO NAWS negotiation after the DO ECHO, and we won't get here 420*38995Sborman * until a response to the DO NAWS comes back) simulate the 421*38995Sborman * receipt of a will echo. This will also send a WONT ECHO 422*38995Sborman * to the client, since we assume that the client failed to 423*38995Sborman * respond because it believes that it is already in DO ECHO 424*38995Sborman * mode, which we do not want. 425*38995Sborman */ 426*38995Sborman if (hiswants[TELOPT_ECHO] == OPT_YES) 427*38995Sborman willoption(TELOPT_ECHO, 0); 428*38995Sborman 429*38995Sborman /* 430*38995Sborman * Finally, to clean things up, we turn on our echo. This 431*38995Sborman * will break stupid 4.2 telnets out of local terminal echo. 432*38995Sborman */ 433*38995Sborman 434*38995Sborman if (!myopts[TELOPT_ECHO]) 435*38995Sborman dooption(TELOPT_ECHO); 436*38995Sborman 437*38995Sborman /* 43838904Sborman * Turn on packet mode, and default to line at at time mode. 43938904Sborman */ 44038904Sborman (void) ioctl(p, TIOCPKT, (char *)&on); 44138904Sborman #ifdef LINEMODE 44238904Sborman tty_setlinemode(1); 44338904Sborman 44438904Sborman # ifdef KLUDGELINEMODE 44538904Sborman /* 44638904Sborman * Continuing line mode support. If client does not support 44738904Sborman * real linemode, attempt to negotiate kludge linemode by sending 44838904Sborman * the do timing mark sequence. 44938904Sborman */ 45038904Sborman if (lmodetype < REAL_LINEMODE) 45138904Sborman willoption(TELOPT_TM, 1); 45238904Sborman # endif /* KLUDGELINEMODE */ 45338904Sborman #endif /* LINEMODE */ 45438904Sborman 45538904Sborman /* 45638904Sborman * Call telrcv() once to pick up anything received during 45738904Sborman * terminal type negotiation, 4.2/4.3 determination, and 45838904Sborman * linemode negotiation. 45938904Sborman */ 46038904Sborman telrcv(); 46138904Sborman 46238904Sborman (void) ioctl(f, FIONBIO, (char *)&on); 46338904Sborman (void) ioctl(p, FIONBIO, (char *)&on); 46438904Sborman #ifdef CRAY2 46538904Sborman init_termdriver(f, p, interrupt, sendbrk); 46638904Sborman #endif 46738904Sborman 46838904Sborman #if defined(SO_OOBINLINE) 46938904Sborman (void) setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on); 47038904Sborman #endif /* defined(SO_OOBINLINE) */ 47138904Sborman 47238904Sborman #ifdef SIGTSTP 47338904Sborman (void) signal(SIGTSTP, SIG_IGN); 47438904Sborman #endif 47538904Sborman #ifdef SIGTTOU 47638904Sborman /* 47738904Sborman * Ignoring SIGTTOU keeps the kernel from blocking us 47838904Sborman * in ttioct() in /sys/tty.c. 47938904Sborman */ 48038904Sborman (void) signal(SIGTTOU, SIG_IGN); 48138904Sborman #endif 48238904Sborman 48338904Sborman (void) signal(SIGCHLD, cleanup); 48438904Sborman 48538904Sborman #if defined(CRAY2) 48638904Sborman /* 48738904Sborman * Cray-2 will send a signal when pty modes are changed by slave 48838904Sborman * side. Set up signal handler now. 48938904Sborman */ 49038904Sborman if ((int)signal(SIGUSR1, termstat) < 0) 49138904Sborman perror("signal"); 49238904Sborman else if (ioctl(p, TCSIGME, (char *)SIGUSR1) < 0) 49338904Sborman perror("ioctl:TCSIGME"); 49438904Sborman /* 49538904Sborman * Make processing loop check terminal characteristics early on. 49638904Sborman */ 49738904Sborman termstat(); 49838904Sborman #endif 49938904Sborman 50038904Sborman (void) setpgrp(0, 0); 50138904Sborman 50238904Sborman /* 50312713Ssam * Show banner that getty never gave. 50427797Sminshall * 50533271Sminshall * We put the banner in the pty input buffer. This way, it 50633271Sminshall * gets carriage return null processing, etc., just like all 50733271Sminshall * other pty --> client data. 50812713Ssam */ 50927797Sminshall 51038904Sborman (void) gethostname(hostname, sizeof (hostname)); 51138904Sborman 51233271Sminshall if (getent(defent, "default") == 1) { 51333271Sminshall char *getstr(); 51438904Sborman char *cp=defstrs; 51527649Sminshall 51638904Sborman HE = getstr("he", &cp); 51738904Sborman HN = getstr("hn", &cp); 51838904Sborman IM = getstr("im", &cp); 51933271Sminshall if (HN && *HN) 52038904Sborman (void) strcpy(hostname, HN); 52138904Sborman if (IM == 0) 52238904Sborman IM = ""; 52333271Sminshall } else { 52438904Sborman #ifdef CRAY 52538904Sborman if (hostinfo == 0) 52638904Sborman IM = 0; 52738904Sborman else 52838904Sborman #endif 52938904Sborman IM = DEFAULT_IM; 53038904Sborman HE = 0; 53133271Sminshall } 53238904Sborman edithost(HE, hostname); 53338904Sborman if (IM && *IM) 53438904Sborman putf(IM, ptyibuf2); 53527797Sminshall 53638904Sborman if (pcc) 53738904Sborman (void) strncat(ptyibuf2, ptyip, pcc+1); 53838904Sborman ptyip = ptyibuf2; 53938904Sborman pcc = strlen(ptyip); 54033271Sminshall 5416002Sroot for (;;) { 54227185Sminshall fd_set ibits, obits, xbits; 5436002Sroot register int c; 5446002Sroot 54527185Sminshall if (ncc < 0 && pcc < 0) 54627185Sminshall break; 54727185Sminshall 54838904Sborman #ifdef CRAY2 54938904Sborman if (needtermstat) 55038904Sborman _termstat(); 55138904Sborman #endif /* CRAY2 */ 55227185Sminshall FD_ZERO(&ibits); 55327185Sminshall FD_ZERO(&obits); 55427185Sminshall FD_ZERO(&xbits); 5556002Sroot /* 5566002Sroot * Never look for input if there's still 5576002Sroot * stuff in the corresponding output buffer 5586002Sroot */ 55927185Sminshall if (nfrontp - nbackp || pcc > 0) { 56027185Sminshall FD_SET(f, &obits); 56127185Sminshall } else { 56227185Sminshall FD_SET(p, &ibits); 56327185Sminshall } 56427185Sminshall if (pfrontp - pbackp || ncc > 0) { 56527185Sminshall FD_SET(p, &obits); 56627185Sminshall } else { 56727185Sminshall FD_SET(f, &ibits); 56827185Sminshall } 56927185Sminshall if (!SYNCHing) { 57027185Sminshall FD_SET(f, &xbits); 57127185Sminshall } 57227185Sminshall if ((c = select(16, &ibits, &obits, &xbits, 57327185Sminshall (struct timeval *)0)) < 1) { 57427185Sminshall if (c == -1) { 57527185Sminshall if (errno == EINTR) { 57627185Sminshall continue; 57727185Sminshall } 57827185Sminshall } 5796002Sroot sleep(5); 5806002Sroot continue; 5816002Sroot } 5826002Sroot 5836002Sroot /* 58427185Sminshall * Any urgent data? 58527185Sminshall */ 58627185Sminshall if (FD_ISSET(net, &xbits)) { 58727185Sminshall SYNCHing = 1; 58827185Sminshall } 58927185Sminshall 59027185Sminshall /* 5916002Sroot * Something to read from the network... 5926002Sroot */ 59327185Sminshall if (FD_ISSET(net, &ibits)) { 59427649Sminshall #if !defined(SO_OOBINLINE) 59527185Sminshall /* 59627898Skarels * In 4.2 (and 4.3 beta) systems, the 59727185Sminshall * OOB indication and data handling in the kernel 59827185Sminshall * is such that if two separate TCP Urgent requests 59927185Sminshall * come in, one byte of TCP data will be overlaid. 60027185Sminshall * This is fatal for Telnet, but we try to live 60127185Sminshall * with it. 60227185Sminshall * 60327185Sminshall * In addition, in 4.2 (and...), a special protocol 60427185Sminshall * is needed to pick up the TCP Urgent data in 60527185Sminshall * the correct sequence. 60627185Sminshall * 60727185Sminshall * What we do is: if we think we are in urgent 60827185Sminshall * mode, we look to see if we are "at the mark". 60927185Sminshall * If we are, we do an OOB receive. If we run 61027185Sminshall * this twice, we will do the OOB receive twice, 61127185Sminshall * but the second will fail, since the second 61227185Sminshall * time we were "at the mark", but there wasn't 61327185Sminshall * any data there (the kernel doesn't reset 61427185Sminshall * "at the mark" until we do a normal read). 61527185Sminshall * Once we've read the OOB data, we go ahead 61627185Sminshall * and do normal reads. 61727185Sminshall * 61827185Sminshall * There is also another problem, which is that 61927185Sminshall * since the OOB byte we read doesn't put us 62027185Sminshall * out of OOB state, and since that byte is most 62127185Sminshall * likely the TELNET DM (data mark), we would 62227185Sminshall * stay in the TELNET SYNCH (SYNCHing) state. 62327185Sminshall * So, clocks to the rescue. If we've "just" 62427185Sminshall * received a DM, then we test for the 62527185Sminshall * presence of OOB data when the receive OOB 62627185Sminshall * fails (and AFTER we did the normal mode read 62727185Sminshall * to clear "at the mark"). 62827185Sminshall */ 62927185Sminshall if (SYNCHing) { 63027185Sminshall int atmark; 63127185Sminshall 63238904Sborman (void) ioctl(net, SIOCATMARK, (char *)&atmark); 63327185Sminshall if (atmark) { 63427185Sminshall ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB); 63527185Sminshall if ((ncc == -1) && (errno == EINVAL)) { 63627185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 63727983Sminshall if (sequenceIs(didnetreceive, gotDM)) { 63827185Sminshall SYNCHing = stilloob(net); 63927185Sminshall } 64027185Sminshall } 64127185Sminshall } else { 64227185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 6436002Sroot } 64427185Sminshall } else { 64527185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 64627185Sminshall } 64727185Sminshall settimer(didnetreceive); 64827649Sminshall #else /* !defined(SO_OOBINLINE)) */ 64927185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 65027649Sminshall #endif /* !defined(SO_OOBINLINE)) */ 65127185Sminshall if (ncc < 0 && errno == EWOULDBLOCK) 65227185Sminshall ncc = 0; 65327185Sminshall else { 65427185Sminshall if (ncc <= 0) { 65527185Sminshall break; 65627185Sminshall } 65727185Sminshall netip = netibuf; 65827185Sminshall } 6596002Sroot } 6606002Sroot 6616002Sroot /* 6626002Sroot * Something to read from the pty... 6636002Sroot */ 66438904Sborman if (FD_ISSET(p, &ibits)) { 6656002Sroot pcc = read(p, ptyibuf, BUFSIZ); 6666002Sroot if (pcc < 0 && errno == EWOULDBLOCK) 6676002Sroot pcc = 0; 6686002Sroot else { 6696002Sroot if (pcc <= 0) 6706002Sroot break; 67138904Sborman #ifndef CRAY2 67238904Sborman #ifdef LINEMODE 67338904Sborman /* 67438904Sborman * If ioctl from pty, pass it through net 67538904Sborman */ 67638904Sborman if (ptyibuf[0] & TIOCPKT_IOCTL) { 67738904Sborman copy_termbuf(ptyibuf+1, pcc-1); 67838904Sborman localstat(); 67938904Sborman pcc = 1; 68038904Sborman } 68138904Sborman #endif LINEMODE 68237210Sminshall if (ptyibuf[0] & TIOCPKT_FLUSHWRITE) { 68338904Sborman netclear(); /* clear buffer back */ 68437210Sminshall *nfrontp++ = IAC; 68537210Sminshall *nfrontp++ = DM; 68637210Sminshall neturg = nfrontp-1; /* off by one XXX */ 68737210Sminshall } 68837210Sminshall if (hisopts[TELOPT_LFLOW] && 68937210Sminshall (ptyibuf[0] & 69038904Sborman (TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))) { 69138904Sborman (void) sprintf(nfrontp, "%c%c%c%c%c%c", 69237210Sminshall IAC, SB, TELOPT_LFLOW, 69337210Sminshall ptyibuf[0] & TIOCPKT_DOSTOP ? 1 : 0, 69437210Sminshall IAC, SE); 69537210Sminshall nfrontp += 6; 69637210Sminshall } 69733267Sminshall pcc--; 69833267Sminshall ptyip = ptyibuf+1; 69938904Sborman #else /* CRAY2 */ 70038904Sborman if (!uselinemode) { 70138904Sborman pcc = term_output(ptyibuf, ptyibuf2, 70238904Sborman pcc, BUFSIZ); 70338904Sborman ptyip = ptyibuf2; 70438904Sborman } else 70538904Sborman ptyip = ptyibuf; 70638904Sborman #endif /* CRAY2 */ 70738904Sborman } 7086002Sroot } 7096002Sroot 7106002Sroot while (pcc > 0) { 7116002Sroot if ((&netobuf[BUFSIZ] - nfrontp) < 2) 7126002Sroot break; 7136002Sroot c = *ptyip++ & 0377, pcc--; 7146002Sroot if (c == IAC) 7156002Sroot *nfrontp++ = c; 71638904Sborman #ifdef CRAY2 71738904Sborman else if (c == '\n' && 71838904Sborman myopts[TELOPT_BINARY] == OPT_NO && newmap) 71938904Sborman *nfrontp++ = '\r'; 72038904Sborman #endif /* CRAY2 */ 7216002Sroot *nfrontp++ = c; 72231940Sbostic if ((c == '\r') && (myopts[TELOPT_BINARY] == OPT_NO)) { 72327020Sminshall if (pcc > 0 && ((*ptyip & 0377) == '\n')) { 72427020Sminshall *nfrontp++ = *ptyip++ & 0377; 72527020Sminshall pcc--; 72627020Sminshall } else 72727020Sminshall *nfrontp++ = '\0'; 72827020Sminshall } 7296002Sroot } 73027185Sminshall if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0) 7316002Sroot netflush(); 7326002Sroot if (ncc > 0) 7336002Sroot telrcv(); 73427185Sminshall if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0) 7356002Sroot ptyflush(); 7366002Sroot } 7376002Sroot cleanup(); 73838904Sborman } /* end of telnet */ 7396002Sroot 74038904Sborman #ifndef TCSIG 74138904Sborman # ifdef TIOCSIG 74238904Sborman # define TCSIG TIOCSIG 74338904Sborman # endif 74438904Sborman #endif 7456002Sroot 74637212Sminshall /* 7476002Sroot * Send interrupt to process on other side of pty. 7486002Sroot * If it is in raw mode, just write NULL; 7496002Sroot * otherwise, write intr char. 7506002Sroot */ 7516002Sroot interrupt() 7526002Sroot { 75338904Sborman ptyflush(); /* half-hearted */ 7546002Sroot 75538904Sborman #ifdef TCSIG 75638904Sborman (void) ioctl(pty, TCSIG, (char *)SIGINT); 75738904Sborman #else /* TCSIG */ 75838904Sborman init_termbuf(); 75938904Sborman *pfrontp++ = slctab[SLC_IP].sptr ? *slctab[SLC_IP].sptr : '\177'; 76038904Sborman #endif /* TCSIG */ 7616002Sroot } 7626002Sroot 76327229Sminshall /* 76427229Sminshall * Send quit to process on other side of pty. 76527229Sminshall * If it is in raw mode, just write NULL; 76627229Sminshall * otherwise, write quit char. 76727229Sminshall */ 76827229Sminshall sendbrk() 76927229Sminshall { 77027229Sminshall ptyflush(); /* half-hearted */ 77138904Sborman #ifdef TCSIG 77238904Sborman (void) ioctl(pty, TCSIG, (char *)SIGQUIT); 77338904Sborman #else /* TCSIG */ 77438904Sborman init_termbuf(); 77538904Sborman *pfrontp++ = slctab[SLC_ABORT].sptr ? *slctab[SLC_ABORT].sptr : '\034'; 77638904Sborman #endif /* TCSIG */ 77727229Sminshall } 77827229Sminshall 77938904Sborman sendsusp() 7806002Sroot { 78138904Sborman #ifdef SIGTSTP 78238904Sborman ptyflush(); /* half-hearted */ 78338904Sborman # ifdef TCSIG 78438904Sborman (void) ioctl(pty, TCSIG, (char *)SIGTSTP); 78538904Sborman # else /* TCSIG */ 78638904Sborman *pfrontp++ = slctab[SLC_SUSP].sptr ? *slctab[SLC_SUSP].sptr : '\032'; 78738904Sborman # endif /* TCSIG */ 78838904Sborman #endif /* SIGTSTP */ 7896002Sroot } 7906002Sroot 79138904Sborman doeof() 7926002Sroot { 79338904Sborman init_termbuf(); 7946002Sroot 79538904Sborman *pfrontp++ = slctab[SLC_EOF].sptr ? *slctab[SLC_EOF].sptr : '\004'; 7966002Sroot } 797